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,21 +1,111 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React, { useMemo } from 'react';
|
|
4
|
-
import { View, Text, Pressable } from 'react-native';
|
|
3
|
+
import React, { useCallback, useMemo, useRef } from 'react';
|
|
4
|
+
import { View, Text, Pressable, Platform } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
6
|
import IconCapsule from '../IconCapsule/IconCapsule';
|
|
7
7
|
import NavArrow from '../NavArrow/NavArrow';
|
|
8
8
|
import { usePressableWebSupport } from '../../utils/web-platform-utils';
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Helper function to recursively clone children and pass modes prop to components that accept it.
|
|
12
|
-
* This ensures that all child components in slots receive the modes prop from the parent.
|
|
13
|
-
*
|
|
14
|
-
* @param forcedModes - Optional modes that will ALWAYS be applied last, overriding any other modes.
|
|
15
|
-
* Useful for slots that need to enforce specific context values.
|
|
16
|
-
*/
|
|
17
|
-
import { cloneChildrenWithModes } from '../../utils/react-utils';
|
|
9
|
+
import { EMPTY_MODES, cloneChildrenWithModes } from '../../utils/react-utils';
|
|
18
10
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
11
|
+
// ---------------------------------------------------------------------------
|
|
12
|
+
// Module-scope constants — never re-allocated per render.
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
const IS_IOS = Platform.OS === 'ios';
|
|
16
|
+
const PRESS_DELAY = IS_IOS ? 130 : 0;
|
|
17
|
+
|
|
18
|
+
// Forced modes for the endSlot — `Context: 'ListItem'` can never be
|
|
19
|
+
// overridden by external modes. Frozen so identity is stable across renders.
|
|
20
|
+
const END_SLOT_FORCED_MODES = Object.freeze({
|
|
21
|
+
Context: 'ListItem'
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Pressed visual is applied on the host view through Pressable's style
|
|
25
|
+
// callback, so a scroll-cancelled touch never schedules a React render.
|
|
26
|
+
const pressedOverlayStyle = {
|
|
27
|
+
opacity: 0.85
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Token resolution — collapsed into one useMemo per (modes) tuple.
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
function resolveListItemTokens(modes) {
|
|
35
|
+
const resolvedModes = {
|
|
36
|
+
...modes,
|
|
37
|
+
Context: 'ListItem'
|
|
38
|
+
};
|
|
39
|
+
const gap = getVariableByName('listItem/gap', resolvedModes) ?? 8;
|
|
40
|
+
const paddingTop = getVariableByName('listItem/padding/top', resolvedModes) ?? 0;
|
|
41
|
+
const paddingBottom = getVariableByName('listItem/padding/bottom', resolvedModes) ?? 0;
|
|
42
|
+
const paddingLeft = getVariableByName('listItem/padding/left', resolvedModes) ?? 0;
|
|
43
|
+
const paddingRight = getVariableByName('listItem/padding/right', resolvedModes) ?? 0;
|
|
44
|
+
const textWrapGap = getVariableByName('listItem/text wrap', resolvedModes) ?? 0;
|
|
45
|
+
const titleColor = getVariableByName('listItem/title/color', resolvedModes) || '#0f0d0a';
|
|
46
|
+
const titleFontSize = getVariableByName('listItem/title/fontSize', resolvedModes) || 14;
|
|
47
|
+
const titleLineHeight = getVariableByName('listItem/title/lineHeight', resolvedModes) || 16;
|
|
48
|
+
const titleFontFamily = getVariableByName('listItem/title/fontFamily', resolvedModes) || 'System';
|
|
49
|
+
const titleFontWeightRaw = getVariableByName('listItem/title/fontWeight', resolvedModes) || 700;
|
|
50
|
+
const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw;
|
|
51
|
+
const supportColor = getVariableByName('listItem/supportText/color', resolvedModes) || '#1f1a14';
|
|
52
|
+
const supportFontSize = getVariableByName('listItem/supportText/fontSize', resolvedModes) || 12;
|
|
53
|
+
const supportLineHeight = getVariableByName('listItem/supportText/lineHeight', resolvedModes) || 14;
|
|
54
|
+
const supportFontFamily = getVariableByName('listItem/supportText/fontFamily', resolvedModes) || 'System';
|
|
55
|
+
const supportFontWeightRaw = getVariableByName('listItem/supportText/fontWeight', resolvedModes) || 500;
|
|
56
|
+
const supportFontWeight = typeof supportFontWeightRaw === 'number' ? supportFontWeightRaw.toString() : supportFontWeightRaw;
|
|
57
|
+
return {
|
|
58
|
+
baseContainerStyle: {
|
|
59
|
+
paddingTop: paddingTop,
|
|
60
|
+
paddingBottom: paddingBottom,
|
|
61
|
+
paddingLeft: paddingLeft,
|
|
62
|
+
paddingRight: paddingRight
|
|
63
|
+
},
|
|
64
|
+
horizontalLayoutStyle: {
|
|
65
|
+
flexDirection: 'row',
|
|
66
|
+
alignItems: 'center',
|
|
67
|
+
gap
|
|
68
|
+
},
|
|
69
|
+
verticalLayoutStyle: {
|
|
70
|
+
flexDirection: 'column',
|
|
71
|
+
alignItems: 'center',
|
|
72
|
+
gap
|
|
73
|
+
},
|
|
74
|
+
innerContentBaseStyle: {
|
|
75
|
+
flexDirection: 'row',
|
|
76
|
+
alignItems: 'center',
|
|
77
|
+
flex: 1,
|
|
78
|
+
gap
|
|
79
|
+
},
|
|
80
|
+
verticalContentBaseStyle: {
|
|
81
|
+
alignItems: 'center',
|
|
82
|
+
gap
|
|
83
|
+
},
|
|
84
|
+
titleTextStyle: {
|
|
85
|
+
color: titleColor,
|
|
86
|
+
fontSize: titleFontSize,
|
|
87
|
+
lineHeight: titleLineHeight,
|
|
88
|
+
fontFamily: titleFontFamily,
|
|
89
|
+
fontWeight: titleFontWeight
|
|
90
|
+
},
|
|
91
|
+
supportTextStyle: {
|
|
92
|
+
color: supportColor,
|
|
93
|
+
fontSize: supportFontSize,
|
|
94
|
+
lineHeight: supportLineHeight,
|
|
95
|
+
fontFamily: supportFontFamily,
|
|
96
|
+
fontWeight: supportFontWeight
|
|
97
|
+
},
|
|
98
|
+
trailingWrapperStyle: {
|
|
99
|
+
marginLeft: gap / 2
|
|
100
|
+
},
|
|
101
|
+
textWrapGap,
|
|
102
|
+
resolvedModes
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const verticalSupportTextOverride = {
|
|
106
|
+
textAlign: 'center'
|
|
107
|
+
};
|
|
108
|
+
|
|
19
109
|
/**
|
|
20
110
|
* ListItem component that mirrors the Figma "List Item" component.
|
|
21
111
|
*
|
|
@@ -47,8 +137,20 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
47
137
|
* @param {string} [props.accessibilityLabel] - Accessibility label for screen readers. If not provided, uses title and supportText
|
|
48
138
|
* @param {string} [props.accessibilityHint] - Additional accessibility hint for screen readers
|
|
49
139
|
* @param {Object} [props.accessibilityState] - Additional accessibility state information
|
|
140
|
+
*
|
|
141
|
+
* Performance notes:
|
|
142
|
+
* - All token reads are folded into a single `useMemo([modes])`.
|
|
143
|
+
* - The pressed visual is applied via Pressable's `style={({pressed}) => ...}`
|
|
144
|
+
* callback so RN updates the host view directly — no React render is
|
|
145
|
+
* scheduled by a touch. On iOS the touch is additionally delayed via
|
|
146
|
+
* `unstable_pressDelay={130}` so a scroll-cancelled tap never even
|
|
147
|
+
* transiently applies the pressed style. This is the fix for the
|
|
148
|
+
* "People / Setup card flicker during scroll" report.
|
|
149
|
+
* - User-supplied event handlers are read through a ref so the wrapper
|
|
150
|
+
* handlers stay referentially stable across renders.
|
|
151
|
+
* - The component is wrapped in `React.memo`.
|
|
50
152
|
*/
|
|
51
|
-
function
|
|
153
|
+
function ListItemImpl({
|
|
52
154
|
layout = 'Vertical',
|
|
53
155
|
title = 'Title',
|
|
54
156
|
supportText = 'Support Text',
|
|
@@ -57,7 +159,7 @@ function ListItem({
|
|
|
57
159
|
supportSlot,
|
|
58
160
|
endSlot,
|
|
59
161
|
navArrow = true,
|
|
60
|
-
modes =
|
|
162
|
+
modes = EMPTY_MODES,
|
|
61
163
|
onPress,
|
|
62
164
|
style,
|
|
63
165
|
contentStyle,
|
|
@@ -67,89 +169,21 @@ function ListItem({
|
|
|
67
169
|
webAccessibilityProps,
|
|
68
170
|
...rest
|
|
69
171
|
}) {
|
|
70
|
-
const
|
|
71
|
-
...modes,
|
|
72
|
-
"Context": "ListItem"
|
|
73
|
-
};
|
|
172
|
+
const tokens = useMemo(() => resolveListItemTokens(modes), [modes]);
|
|
74
173
|
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const textWrapGap = getVariableByName('listItem/text wrap', resolvedModes) ?? 0;
|
|
82
|
-
|
|
83
|
-
// Title typography (horizontal layout)
|
|
84
|
-
const titleColor = getVariableByName('listItem/title/color', resolvedModes) || '#0f0d0a';
|
|
85
|
-
const titleFontSize = getVariableByName('listItem/title/fontSize', resolvedModes) || 14;
|
|
86
|
-
const titleLineHeight = getVariableByName('listItem/title/lineHeight', resolvedModes) || 16;
|
|
87
|
-
const titleFontFamily = getVariableByName('listItem/title/fontFamily', resolvedModes) || 'System';
|
|
88
|
-
const titleFontWeightRaw = getVariableByName('listItem/title/fontWeight', resolvedModes) || 700;
|
|
89
|
-
const titleFontWeight = typeof titleFontWeightRaw === 'number' ? titleFontWeightRaw.toString() : titleFontWeightRaw;
|
|
90
|
-
|
|
91
|
-
// Support text typography (used in both layouts)
|
|
92
|
-
const supportColor = getVariableByName('listItem/supportText/color', resolvedModes) || '#1f1a14';
|
|
93
|
-
const supportFontSize = getVariableByName('listItem/supportText/fontSize', resolvedModes) || 12;
|
|
94
|
-
const supportLineHeight = getVariableByName('listItem/supportText/lineHeight', resolvedModes) || 14;
|
|
95
|
-
const supportFontFamily = getVariableByName('listItem/supportText/fontFamily', resolvedModes) || 'System';
|
|
96
|
-
const supportFontWeightRaw = getVariableByName('listItem/supportText/fontWeight', resolvedModes) || 500;
|
|
97
|
-
const supportFontWeight = typeof supportFontWeightRaw === 'number' ? supportFontWeightRaw.toString() : supportFontWeightRaw;
|
|
98
|
-
const baseContainerStyle = {
|
|
99
|
-
paddingTop,
|
|
100
|
-
paddingBottom,
|
|
101
|
-
paddingLeft,
|
|
102
|
-
paddingRight
|
|
103
|
-
};
|
|
104
|
-
const sharedLayoutStyle = layout === 'Horizontal' ? {
|
|
105
|
-
flexDirection: 'row',
|
|
106
|
-
alignItems: 'center',
|
|
107
|
-
gap
|
|
108
|
-
} : {
|
|
109
|
-
flexDirection: 'column',
|
|
110
|
-
alignItems: 'center',
|
|
111
|
-
gap
|
|
112
|
-
};
|
|
113
|
-
const innerContentBaseStyle = {
|
|
114
|
-
flexDirection: 'row',
|
|
115
|
-
alignItems: 'center',
|
|
116
|
-
flex: 1,
|
|
117
|
-
gap
|
|
118
|
-
};
|
|
119
|
-
const verticalContentBaseStyle = {
|
|
120
|
-
alignItems: 'center',
|
|
121
|
-
gap
|
|
122
|
-
};
|
|
123
|
-
const titleTextStyle = {
|
|
124
|
-
color: titleColor,
|
|
125
|
-
fontSize: titleFontSize,
|
|
126
|
-
lineHeight: titleLineHeight,
|
|
127
|
-
fontFamily: titleFontFamily,
|
|
128
|
-
fontWeight: titleFontWeight
|
|
129
|
-
};
|
|
130
|
-
const supportTextStyle = {
|
|
131
|
-
color: supportColor,
|
|
132
|
-
fontSize: supportFontSize,
|
|
133
|
-
lineHeight: supportLineHeight,
|
|
134
|
-
fontFamily: supportFontFamily,
|
|
135
|
-
fontWeight: supportFontWeight
|
|
136
|
-
};
|
|
137
|
-
const trailingWrapperStyle = useMemo(() => ({
|
|
138
|
-
marginLeft: gap / 2
|
|
139
|
-
}), [gap]);
|
|
174
|
+
// Mirror user-supplied handlers in a ref so our wrappers can stay
|
|
175
|
+
// referentially stable. Without this, every parent re-render hands
|
|
176
|
+
// Pressable a fresh function identity for each event prop.
|
|
177
|
+
const userHandlersRef = useRef({});
|
|
178
|
+
userHandlersRef.current.onPressIn = rest?.onPressIn;
|
|
179
|
+
userHandlersRef.current.onPressOut = rest?.onPressOut;
|
|
140
180
|
|
|
141
181
|
// Generate default accessibility label from title and supportText if not provided
|
|
142
182
|
const defaultAccessibilityLabel = accessibilityLabel || (layout === 'Horizontal' ? `${title}${showSupportText && supportText ? `, ${supportText}` : ''}` : supportText);
|
|
143
183
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
onPress,
|
|
148
|
-
disabled: false,
|
|
149
|
-
accessibilityLabel: defaultAccessibilityLabel,
|
|
150
|
-
webAccessibilityProps
|
|
151
|
-
});
|
|
152
|
-
const webPropsVertical = usePressableWebSupport({
|
|
184
|
+
// Single web-platform support payload — the previous version called
|
|
185
|
+
// `usePressableWebSupport` twice with identical args, which was dead work.
|
|
186
|
+
const webProps = usePressableWebSupport({
|
|
153
187
|
restProps: rest,
|
|
154
188
|
onPress,
|
|
155
189
|
disabled: false,
|
|
@@ -157,60 +191,79 @@ function ListItem({
|
|
|
157
191
|
webAccessibilityProps
|
|
158
192
|
});
|
|
159
193
|
|
|
160
|
-
// Process leading slot to pass modes to children
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Process supportSlot to pass modes to children
|
|
170
|
-
const processedSupportSlot = cloneChildrenWithModes(React.Children.toArray(supportSlot), resolvedModes);
|
|
171
|
-
// Extract single element if array has one element, otherwise use array
|
|
172
|
-
return processedSupportSlot.length === 1 ? processedSupportSlot[0] : processedSupportSlot;
|
|
194
|
+
// Process leading slot to pass modes to children. Memoized on
|
|
195
|
+
// (leading, resolvedModes) so a parent re-render doesn't re-walk the tree.
|
|
196
|
+
const leadingElement = useMemo(() => {
|
|
197
|
+
const processed = leading ? cloneChildrenWithModes(React.Children.toArray(leading), tokens.resolvedModes) : [];
|
|
198
|
+
if (processed.length === 0) {
|
|
199
|
+
return /*#__PURE__*/_jsx(IconCapsule, {
|
|
200
|
+
modes: tokens.resolvedModes,
|
|
201
|
+
accessibilityLabel: undefined
|
|
202
|
+
});
|
|
173
203
|
}
|
|
204
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
205
|
+
}, [leading, tokens.resolvedModes]);
|
|
206
|
+
const processedSupportSlot = useMemo(() => {
|
|
207
|
+
if (!supportSlot) return null;
|
|
208
|
+
const processed = cloneChildrenWithModes(React.Children.toArray(supportSlot), tokens.resolvedModes);
|
|
209
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
210
|
+
}, [supportSlot, tokens.resolvedModes]);
|
|
211
|
+
const processedEndSlot = useMemo(() => {
|
|
212
|
+
if (!endSlot) return null;
|
|
213
|
+
const processed = cloneChildrenWithModes(React.Children.toArray(endSlot), tokens.resolvedModes, END_SLOT_FORCED_MODES);
|
|
214
|
+
return processed.length === 1 ? processed[0] : processed;
|
|
215
|
+
}, [endSlot, tokens.resolvedModes]);
|
|
216
|
+
const renderSupportContent = () => {
|
|
217
|
+
if (processedSupportSlot) return processedSupportSlot;
|
|
174
218
|
|
|
175
|
-
// Default support text
|
|
176
|
-
// - vertical layout main text
|
|
177
|
-
// - horizontal layout secondary line)
|
|
219
|
+
// Default support text:
|
|
220
|
+
// - vertical layout: main text (line-broken on spaces)
|
|
221
|
+
// - horizontal layout: secondary line (clamped to 2 lines)
|
|
178
222
|
const displayText = layout === 'Vertical' ? supportText.replace(/ /g, '\n') : supportText;
|
|
179
223
|
return /*#__PURE__*/_jsx(Text, {
|
|
180
|
-
style:
|
|
181
|
-
textAlign: 'center'
|
|
182
|
-
} : undefined],
|
|
224
|
+
style: layout === 'Vertical' ? [tokens.supportTextStyle, verticalSupportTextOverride] : tokens.supportTextStyle,
|
|
183
225
|
numberOfLines: layout === 'Horizontal' ? 2 : undefined,
|
|
184
226
|
children: displayText
|
|
185
227
|
});
|
|
186
228
|
};
|
|
229
|
+
|
|
230
|
+
// Stable handlers — user-supplied callbacks are forwarded via ref.
|
|
231
|
+
const handlePressIn = useCallback(e => {
|
|
232
|
+
userHandlersRef.current.onPressIn?.(e);
|
|
233
|
+
}, []);
|
|
234
|
+
const handlePressOut = useCallback(e => {
|
|
235
|
+
userHandlersRef.current.onPressOut?.(e);
|
|
236
|
+
}, []);
|
|
237
|
+
const horizontalContainerStyleArray = useMemo(() => [tokens.baseContainerStyle, tokens.horizontalLayoutStyle, style], [tokens.baseContainerStyle, tokens.horizontalLayoutStyle, style]);
|
|
238
|
+
const verticalContainerStyleArray = useMemo(() => [tokens.baseContainerStyle, tokens.verticalLayoutStyle, style], [tokens.baseContainerStyle, tokens.verticalLayoutStyle, style]);
|
|
239
|
+
const horizontalPressableStyle = useCallback(({
|
|
240
|
+
pressed
|
|
241
|
+
}) => [tokens.baseContainerStyle, tokens.horizontalLayoutStyle, pressed ? pressedOverlayStyle : null, style], [tokens.baseContainerStyle, tokens.horizontalLayoutStyle, style]);
|
|
242
|
+
const verticalPressableStyle = useCallback(({
|
|
243
|
+
pressed
|
|
244
|
+
}) => [tokens.baseContainerStyle, tokens.verticalLayoutStyle, pressed ? pressedOverlayStyle : null, style], [tokens.baseContainerStyle, tokens.verticalLayoutStyle, style]);
|
|
245
|
+
const innerContentStyleArray = useMemo(() => [tokens.innerContentBaseStyle, contentStyle], [tokens.innerContentBaseStyle, contentStyle]);
|
|
246
|
+
const verticalContentStyleArray = useMemo(() => [tokens.verticalContentBaseStyle, contentStyle], [tokens.verticalContentBaseStyle, contentStyle]);
|
|
187
247
|
if (layout === 'Horizontal') {
|
|
188
|
-
// Process endSlot to pass modes to children
|
|
189
|
-
// Force Context: 'ListItem' - this value can never be overridden by external modes
|
|
190
|
-
const processedEndSlot = endSlot ? cloneChildrenWithModes(React.Children.toArray(endSlot), resolvedModes, {
|
|
191
|
-
"Context": 'ListItem'
|
|
192
|
-
}) : [];
|
|
193
|
-
// Extract single element if array has one element, otherwise use array
|
|
194
|
-
const trailingContent = processedEndSlot.length > 0 ? processedEndSlot.length === 1 ? processedEndSlot[0] : processedEndSlot : null;
|
|
195
248
|
const innerContent = /*#__PURE__*/_jsxs(View, {
|
|
196
|
-
style:
|
|
249
|
+
style: innerContentStyleArray,
|
|
197
250
|
children: [leadingElement, /*#__PURE__*/_jsxs(View, {
|
|
198
251
|
style: {
|
|
199
252
|
flex: 1,
|
|
200
253
|
minWidth: 1,
|
|
201
|
-
gap: textWrapGap
|
|
254
|
+
gap: tokens.textWrapGap
|
|
202
255
|
},
|
|
203
256
|
children: [/*#__PURE__*/_jsx(Text, {
|
|
204
|
-
style: titleTextStyle,
|
|
257
|
+
style: tokens.titleTextStyle,
|
|
205
258
|
numberOfLines: 1,
|
|
206
259
|
children: title
|
|
207
260
|
}), showSupportText && renderSupportContent()]
|
|
208
|
-
}),
|
|
209
|
-
style: trailingWrapperStyle,
|
|
210
|
-
children:
|
|
261
|
+
}), processedEndSlot ? /*#__PURE__*/_jsx(View, {
|
|
262
|
+
style: tokens.trailingWrapperStyle,
|
|
263
|
+
children: processedEndSlot
|
|
211
264
|
}) : null, navArrow && /*#__PURE__*/_jsx(NavArrow, {
|
|
212
265
|
direction: "Forward",
|
|
213
|
-
modes: resolvedModes
|
|
266
|
+
modes: tokens.resolvedModes
|
|
214
267
|
})]
|
|
215
268
|
});
|
|
216
269
|
if (onPress) {
|
|
@@ -218,16 +271,13 @@ function ListItem({
|
|
|
218
271
|
accessibilityRole: "button",
|
|
219
272
|
accessibilityLabel: undefined,
|
|
220
273
|
accessibilityHint: accessibilityHint,
|
|
221
|
-
accessibilityState:
|
|
222
|
-
...accessibilityState
|
|
223
|
-
},
|
|
274
|
+
accessibilityState: accessibilityState,
|
|
224
275
|
onPress: onPress,
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
...webPropsHorizontal,
|
|
276
|
+
onPressIn: handlePressIn,
|
|
277
|
+
onPressOut: handlePressOut,
|
|
278
|
+
unstable_pressDelay: PRESS_DELAY,
|
|
279
|
+
style: horizontalPressableStyle,
|
|
280
|
+
...webProps,
|
|
231
281
|
children: innerContent
|
|
232
282
|
});
|
|
233
283
|
}
|
|
@@ -235,15 +285,15 @@ function ListItem({
|
|
|
235
285
|
accessibilityRole: undefined,
|
|
236
286
|
accessibilityLabel: undefined,
|
|
237
287
|
accessibilityHint: accessibilityHint,
|
|
238
|
-
style:
|
|
288
|
+
style: horizontalContainerStyleArray,
|
|
239
289
|
...rest,
|
|
240
290
|
children: innerContent
|
|
241
291
|
});
|
|
242
292
|
}
|
|
243
293
|
|
|
244
|
-
// Vertical layout
|
|
294
|
+
// Vertical layout — icon on top, support text/slot below
|
|
245
295
|
const verticalContent = /*#__PURE__*/_jsxs(View, {
|
|
246
|
-
style:
|
|
296
|
+
style: verticalContentStyleArray,
|
|
247
297
|
children: [leadingElement, renderSupportContent()]
|
|
248
298
|
});
|
|
249
299
|
if (onPress) {
|
|
@@ -251,16 +301,13 @@ function ListItem({
|
|
|
251
301
|
accessibilityRole: "button",
|
|
252
302
|
accessibilityLabel: undefined,
|
|
253
303
|
accessibilityHint: accessibilityHint,
|
|
254
|
-
accessibilityState:
|
|
255
|
-
...accessibilityState
|
|
256
|
-
},
|
|
304
|
+
accessibilityState: accessibilityState,
|
|
257
305
|
onPress: onPress,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
...webPropsVertical,
|
|
306
|
+
onPressIn: handlePressIn,
|
|
307
|
+
onPressOut: handlePressOut,
|
|
308
|
+
unstable_pressDelay: PRESS_DELAY,
|
|
309
|
+
style: verticalPressableStyle,
|
|
310
|
+
...webProps,
|
|
264
311
|
children: verticalContent
|
|
265
312
|
});
|
|
266
313
|
}
|
|
@@ -268,9 +315,10 @@ function ListItem({
|
|
|
268
315
|
accessibilityRole: undefined,
|
|
269
316
|
accessibilityLabel: undefined,
|
|
270
317
|
accessibilityHint: accessibilityHint,
|
|
271
|
-
style:
|
|
318
|
+
style: verticalContainerStyleArray,
|
|
272
319
|
...rest,
|
|
273
320
|
children: verticalContent
|
|
274
321
|
});
|
|
275
322
|
}
|
|
323
|
+
const ListItem = /*#__PURE__*/React.memo(ListItemImpl);
|
|
276
324
|
export default ListItem;
|
|
@@ -3,10 +3,12 @@
|
|
|
3
3
|
import React, { createContext, useContext, isValidElement, cloneElement } from 'react';
|
|
4
4
|
import { View, Text, StyleSheet, Platform } from 'react-native';
|
|
5
5
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
6
|
-
import {
|
|
6
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
|
+
|
|
7
8
|
/**
|
|
8
9
|
* Context to share 'modes' with child components.
|
|
9
10
|
*/
|
|
11
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
10
12
|
const MediaCardContext = /*#__PURE__*/createContext({});
|
|
11
13
|
/**
|
|
12
14
|
* MediaCard component implementation from Figma node 1241:4140.
|
|
@@ -16,7 +18,7 @@ const MediaCardContext = /*#__PURE__*/createContext({});
|
|
|
16
18
|
export function MediaCard({
|
|
17
19
|
media,
|
|
18
20
|
children,
|
|
19
|
-
modes =
|
|
21
|
+
modes = EMPTY_MODES,
|
|
20
22
|
style
|
|
21
23
|
}) {
|
|
22
24
|
// Container Tokens
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React 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
|
import Avatar from '../Avatar/Avatar';
|
|
7
8
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
9
|
/**
|
|
@@ -44,7 +45,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
44
45
|
function MerchantProfile({
|
|
45
46
|
title = 'Uber India Systems Private Limited',
|
|
46
47
|
subtitle = 'UPI ID: uberindia@ybl',
|
|
47
|
-
modes =
|
|
48
|
+
modes = EMPTY_MODES,
|
|
48
49
|
style,
|
|
49
50
|
avatarProps,
|
|
50
51
|
accessibilityLabel
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import React, { useMemo, useEffect, useRef } from 'react';
|
|
4
4
|
import { View, Text, Pressable, Animated } 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
|
// Map of common ISO 4217 currency codes to display symbols
|
|
8
9
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
@@ -56,7 +57,7 @@ function MoneyValue({
|
|
|
56
57
|
negative,
|
|
57
58
|
focused = false,
|
|
58
59
|
hidden = false,
|
|
59
|
-
modes =
|
|
60
|
+
modes = EMPTY_MODES,
|
|
60
61
|
style,
|
|
61
62
|
valueStyle,
|
|
62
63
|
currencyStyle,
|
|
@@ -1,31 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
|
-
import React from 'react';
|
|
3
|
+
import React, { useMemo } from 'react';
|
|
4
4
|
import { View } from 'react-native';
|
|
5
5
|
import Svg, { Polyline } from 'react-native-svg';
|
|
6
6
|
import { getVariableByName } from '../../design-tokens/figma-variables-resolver';
|
|
7
|
+
import { EMPTY_MODES } from '../../utils/react-utils';
|
|
7
8
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
8
|
-
|
|
9
|
-
* NavArrow component that displays a chevron arrow for navigation.
|
|
10
|
-
*
|
|
11
|
-
* Renders a stroked SVG chevron whose dimensions and thickness are
|
|
12
|
-
* fully driven by design tokens:
|
|
13
|
-
* - navArrow/icon/color - chevron stroke color
|
|
14
|
-
* - navArrow/icon/width - chevron arm width (horizontal spread)
|
|
15
|
-
* - navArrow/icon/height - chevron arm height (vertical spread)
|
|
16
|
-
* - navArrow/icon/strokeWeight - stroke thickness
|
|
17
|
-
* - navArrow/width - container width
|
|
18
|
-
* - navArrow/height - container height
|
|
19
|
-
* - navArrow/radius - border radius
|
|
20
|
-
* - navArrow/background - background color
|
|
21
|
-
*/
|
|
22
|
-
export default function NavArrow({
|
|
23
|
-
direction = 'Back',
|
|
24
|
-
modes = {},
|
|
25
|
-
style,
|
|
26
|
-
accessibilityLabel,
|
|
27
|
-
...rest
|
|
28
|
-
}) {
|
|
9
|
+
function resolveNavArrowTokens(modes) {
|
|
29
10
|
const iconColor = getVariableByName('navArrow/icon/color', modes) || '#24262b';
|
|
30
11
|
const widthToken = Number(getVariableByName('navArrow/width', modes)) || 6;
|
|
31
12
|
const heightToken = Number(getVariableByName('navArrow/height', modes)) || 10;
|
|
@@ -34,54 +15,97 @@ export default function NavArrow({
|
|
|
34
15
|
const iconWidth = Number(getVariableByName('navArrow/icon/width', modes)) || 4;
|
|
35
16
|
const iconHeight = Number(getVariableByName('navArrow/icon/height', modes)) || 8;
|
|
36
17
|
const strokeWeight = Number(getVariableByName('navArrow/icon/strokeWeight', modes)) || 2;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
return {
|
|
19
|
+
iconColor,
|
|
20
|
+
iconWidth,
|
|
21
|
+
iconHeight,
|
|
22
|
+
strokeWeight,
|
|
23
|
+
widthToken,
|
|
24
|
+
heightToken,
|
|
43
25
|
borderRadius,
|
|
44
|
-
backgroundColor
|
|
45
|
-
alignItems: 'center',
|
|
46
|
-
justifyContent: 'center',
|
|
47
|
-
...(style || {})
|
|
26
|
+
backgroundColor
|
|
48
27
|
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* NavArrow component that displays a chevron arrow for navigation.
|
|
32
|
+
*
|
|
33
|
+
* Renders a stroked SVG chevron whose dimensions and thickness are
|
|
34
|
+
* fully driven by design tokens.
|
|
35
|
+
*
|
|
36
|
+
* Performance notes:
|
|
37
|
+
* - Token reads collapsed into a single `useMemo([modes])` so a parent
|
|
38
|
+
* re-render (e.g. from a Section header press) doesn't re-do ~9 token
|
|
39
|
+
* lookups per chevron.
|
|
40
|
+
* - Container + SVG layout values are derived in a second `useMemo` keyed
|
|
41
|
+
* on (tokens, direction, style).
|
|
42
|
+
* - Wrapped in `React.memo`.
|
|
43
|
+
*/
|
|
44
|
+
function NavArrow({
|
|
45
|
+
direction = 'Back',
|
|
46
|
+
modes = EMPTY_MODES,
|
|
47
|
+
style,
|
|
48
|
+
accessibilityLabel,
|
|
49
|
+
...rest
|
|
50
|
+
}) {
|
|
51
|
+
const tokens = useMemo(() => resolveNavArrowTokens(modes), [modes]);
|
|
52
|
+
const computed = useMemo(() => {
|
|
53
|
+
const isDown = direction === 'Down';
|
|
54
|
+
const containerWidth = isDown ? tokens.heightToken : tokens.widthToken;
|
|
55
|
+
const containerHeight = isDown ? tokens.widthToken : tokens.heightToken;
|
|
56
|
+
const containerStyle = {
|
|
57
|
+
width: containerWidth,
|
|
58
|
+
height: containerHeight,
|
|
59
|
+
borderRadius: tokens.borderRadius,
|
|
60
|
+
backgroundColor: tokens.backgroundColor,
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
justifyContent: 'center',
|
|
63
|
+
...(style || {})
|
|
64
|
+
};
|
|
65
|
+
const chevronW = isDown ? tokens.iconHeight : tokens.iconWidth;
|
|
66
|
+
const chevronH = isDown ? tokens.iconWidth : tokens.iconHeight;
|
|
67
|
+
const pad = tokens.strokeWeight / 2;
|
|
68
|
+
const svgWidth = chevronW + pad * 2;
|
|
69
|
+
const svgHeight = chevronH + pad * 2;
|
|
70
|
+
let points;
|
|
71
|
+
switch (direction) {
|
|
72
|
+
case 'Forward':
|
|
73
|
+
points = `${pad},${pad} ${chevronW + pad},${chevronH / 2 + pad} ${pad},${chevronH + pad}`;
|
|
74
|
+
break;
|
|
75
|
+
case 'Down':
|
|
76
|
+
points = `${pad},${pad} ${chevronW / 2 + pad},${chevronH + pad} ${chevronW + pad},${pad}`;
|
|
77
|
+
break;
|
|
78
|
+
case 'Back':
|
|
79
|
+
default:
|
|
80
|
+
points = `${chevronW + pad},${pad} ${pad},${chevronH / 2 + pad} ${chevronW + pad},${chevronH + pad}`;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
containerStyle,
|
|
85
|
+
svgWidth,
|
|
86
|
+
svgHeight,
|
|
87
|
+
points
|
|
88
|
+
};
|
|
89
|
+
}, [tokens, direction, style]);
|
|
49
90
|
const defaultAccessibilityLabel = accessibilityLabel || (direction === 'Back' ? 'Go back' : direction === 'Forward' ? 'Go forward' : 'Go down');
|
|
50
|
-
const chevronW = isDown ? iconHeight : iconWidth;
|
|
51
|
-
const chevronH = isDown ? iconWidth : iconHeight;
|
|
52
|
-
const pad = strokeWeight / 2;
|
|
53
|
-
const svgWidth = chevronW + pad * 2;
|
|
54
|
-
const svgHeight = chevronH + pad * 2;
|
|
55
|
-
let points;
|
|
56
|
-
switch (direction) {
|
|
57
|
-
case 'Forward':
|
|
58
|
-
points = `${pad},${pad} ${chevronW + pad},${chevronH / 2 + pad} ${pad},${chevronH + pad}`;
|
|
59
|
-
break;
|
|
60
|
-
case 'Down':
|
|
61
|
-
points = `${pad},${pad} ${chevronW / 2 + pad},${chevronH + pad} ${chevronW + pad},${pad}`;
|
|
62
|
-
break;
|
|
63
|
-
case 'Back':
|
|
64
|
-
default:
|
|
65
|
-
points = `${chevronW + pad},${pad} ${pad},${chevronH / 2 + pad} ${chevronW + pad},${chevronH + pad}`;
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
91
|
return /*#__PURE__*/_jsx(View, {
|
|
69
|
-
style: containerStyle,
|
|
92
|
+
style: computed.containerStyle,
|
|
70
93
|
accessibilityRole: "image",
|
|
71
94
|
accessibilityLabel: defaultAccessibilityLabel,
|
|
72
95
|
...rest,
|
|
73
96
|
children: /*#__PURE__*/_jsx(Svg, {
|
|
74
|
-
width: svgWidth,
|
|
75
|
-
height: svgHeight,
|
|
76
|
-
viewBox: `0 0 ${svgWidth} ${svgHeight}`,
|
|
97
|
+
width: computed.svgWidth,
|
|
98
|
+
height: computed.svgHeight,
|
|
99
|
+
viewBox: `0 0 ${computed.svgWidth} ${computed.svgHeight}`,
|
|
77
100
|
children: /*#__PURE__*/_jsx(Polyline, {
|
|
78
|
-
points: points,
|
|
79
|
-
stroke: iconColor,
|
|
80
|
-
strokeWidth: strokeWeight,
|
|
101
|
+
points: computed.points,
|
|
102
|
+
stroke: tokens.iconColor,
|
|
103
|
+
strokeWidth: tokens.strokeWeight,
|
|
81
104
|
strokeLinecap: "round",
|
|
82
105
|
strokeLinejoin: "round",
|
|
83
106
|
fill: "none"
|
|
84
107
|
})
|
|
85
108
|
})
|
|
86
109
|
});
|
|
87
|
-
}
|
|
110
|
+
}
|
|
111
|
+
export default /*#__PURE__*/React.memo(NavArrow);
|