@retray-dev/ui-kit 6.1.0 → 7.0.1
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/COMPONENTS.md +447 -13
- package/EXAMPLES.md +248 -0
- package/README.md +11 -10
- package/dist/Accordion.d.mts +28 -0
- package/dist/Accordion.d.ts +28 -0
- package/dist/Accordion.js +340 -0
- package/dist/Accordion.mjs +6 -0
- package/dist/AlertBanner.d.mts +16 -0
- package/dist/AlertBanner.d.ts +16 -0
- package/dist/AlertBanner.js +247 -0
- package/dist/AlertBanner.mjs +5 -0
- package/dist/Avatar.d.mts +20 -0
- package/dist/Avatar.d.ts +20 -0
- package/dist/Avatar.js +234 -0
- package/dist/Avatar.mjs +3 -0
- package/dist/Badge.d.mts +26 -0
- package/dist/Badge.d.ts +26 -0
- package/dist/Badge.js +247 -0
- package/dist/Badge.mjs +4 -0
- package/dist/Button.d.mts +25 -0
- package/dist/Button.d.ts +25 -0
- package/dist/Button.js +414 -0
- package/dist/Button.mjs +8 -0
- package/dist/ButtonGroup.d.mts +26 -0
- package/dist/ButtonGroup.d.ts +26 -0
- package/dist/ButtonGroup.js +52 -0
- package/dist/ButtonGroup.mjs +2 -0
- package/dist/Card.d.mts +39 -0
- package/dist/Card.d.ts +39 -0
- package/dist/Card.js +329 -0
- package/dist/Card.mjs +7 -0
- package/dist/CategoryStrip.d.mts +26 -0
- package/dist/CategoryStrip.d.ts +26 -0
- package/dist/CategoryStrip.js +396 -0
- package/dist/CategoryStrip.mjs +9 -0
- package/dist/Checkbox.d.mts +14 -0
- package/dist/Checkbox.d.ts +14 -0
- package/dist/Checkbox.js +304 -0
- package/dist/Checkbox.mjs +7 -0
- package/dist/Chip.d.mts +31 -0
- package/dist/Chip.d.ts +31 -0
- package/dist/Chip.js +370 -0
- package/dist/Chip.mjs +8 -0
- package/dist/ConfirmDialog.d.mts +15 -0
- package/dist/ConfirmDialog.d.ts +15 -0
- package/dist/ConfirmDialog.js +530 -0
- package/dist/ConfirmDialog.mjs +9 -0
- package/dist/CurrencyDisplay.d.mts +24 -0
- package/dist/CurrencyDisplay.d.ts +24 -0
- package/dist/CurrencyDisplay.js +189 -0
- package/dist/CurrencyDisplay.mjs +3 -0
- package/dist/CurrencyInput.d.mts +26 -0
- package/dist/CurrencyInput.d.ts +26 -0
- package/dist/CurrencyInput.js +404 -0
- package/dist/CurrencyInput.mjs +7 -0
- package/dist/DetailRow.d.mts +32 -0
- package/dist/DetailRow.d.ts +32 -0
- package/dist/DetailRow.js +275 -0
- package/dist/DetailRow.mjs +4 -0
- package/dist/EmptyState.d.mts +27 -0
- package/dist/EmptyState.d.ts +27 -0
- package/dist/EmptyState.js +503 -0
- package/dist/EmptyState.mjs +9 -0
- package/dist/Form.d.mts +52 -0
- package/dist/Form.d.ts +52 -0
- package/dist/Form.js +204 -0
- package/dist/Form.mjs +3 -0
- package/dist/IconButton.d.mts +22 -0
- package/dist/IconButton.d.ts +22 -0
- package/dist/IconButton.js +383 -0
- package/dist/IconButton.mjs +7 -0
- package/dist/Input.d.mts +23 -0
- package/dist/Input.d.ts +23 -0
- package/dist/Input.js +351 -0
- package/dist/Input.mjs +6 -0
- package/dist/LabelValue.d.mts +16 -0
- package/dist/LabelValue.d.ts +16 -0
- package/dist/LabelValue.js +225 -0
- package/dist/LabelValue.mjs +4 -0
- package/dist/ListGroup.d.mts +34 -0
- package/dist/ListGroup.d.ts +34 -0
- package/dist/ListGroup.js +217 -0
- package/dist/ListGroup.mjs +4 -0
- package/dist/ListItem.d.mts +64 -0
- package/dist/ListItem.d.ts +64 -0
- package/dist/ListItem.js +430 -0
- package/dist/ListItem.mjs +8 -0
- package/dist/MediaCard.d.mts +39 -0
- package/dist/MediaCard.d.ts +39 -0
- package/dist/MediaCard.js +427 -0
- package/dist/MediaCard.mjs +8 -0
- package/dist/MenuGroup.d.mts +34 -0
- package/dist/MenuGroup.d.ts +34 -0
- package/dist/MenuGroup.js +217 -0
- package/dist/MenuGroup.mjs +4 -0
- package/dist/MenuItem.d.mts +48 -0
- package/dist/MenuItem.d.ts +48 -0
- package/dist/MenuItem.js +403 -0
- package/dist/MenuItem.mjs +8 -0
- package/dist/MonthPicker.d.mts +20 -0
- package/dist/MonthPicker.d.ts +20 -0
- package/dist/MonthPicker.js +234 -0
- package/dist/MonthPicker.mjs +4 -0
- package/dist/Pressable.d.mts +34 -0
- package/dist/Pressable.d.ts +34 -0
- package/dist/Pressable.js +132 -0
- package/dist/Pressable.mjs +4 -0
- package/dist/Progress.d.mts +14 -0
- package/dist/Progress.d.ts +14 -0
- package/dist/Progress.js +191 -0
- package/dist/Progress.mjs +4 -0
- package/dist/RadioGroup.d.mts +19 -0
- package/dist/RadioGroup.d.ts +19 -0
- package/dist/RadioGroup.js +341 -0
- package/dist/RadioGroup.mjs +7 -0
- package/dist/Select.d.mts +22 -0
- package/dist/Select.d.ts +22 -0
- package/dist/Select.js +441 -0
- package/dist/Select.mjs +6 -0
- package/dist/Separator.d.mts +10 -0
- package/dist/Separator.d.ts +10 -0
- package/dist/Separator.js +156 -0
- package/dist/Separator.mjs +2 -0
- package/dist/Sheet.d.mts +81 -0
- package/dist/Sheet.d.ts +81 -0
- package/dist/Sheet.js +340 -0
- package/dist/Sheet.mjs +4 -0
- package/dist/Skeleton.d.mts +17 -0
- package/dist/Skeleton.d.ts +17 -0
- package/dist/Skeleton.js +205 -0
- package/dist/Skeleton.mjs +4 -0
- package/dist/Slider.d.mts +20 -0
- package/dist/Slider.d.ts +20 -0
- package/dist/Slider.js +232 -0
- package/dist/Slider.mjs +4 -0
- package/dist/Spinner.d.mts +12 -0
- package/dist/Spinner.d.ts +12 -0
- package/dist/Spinner.js +172 -0
- package/dist/Spinner.mjs +3 -0
- package/dist/Switch.d.mts +13 -0
- package/dist/Switch.d.ts +13 -0
- package/dist/Switch.js +261 -0
- package/dist/Switch.mjs +5 -0
- package/dist/Tabs.d.mts +27 -0
- package/dist/Tabs.d.ts +27 -0
- package/dist/Tabs.js +389 -0
- package/dist/Tabs.mjs +6 -0
- package/dist/Text.d.mts +12 -0
- package/dist/Text.d.ts +12 -0
- package/dist/Text.js +311 -0
- package/dist/Text.mjs +4 -0
- package/dist/Textarea.d.mts +16 -0
- package/dist/Textarea.d.ts +16 -0
- package/dist/Textarea.js +333 -0
- package/dist/Textarea.mjs +6 -0
- package/dist/Toast.d.mts +47 -0
- package/dist/Toast.d.ts +47 -0
- package/dist/Toast.js +185 -0
- package/dist/Toast.mjs +3 -0
- package/dist/Toggle.d.mts +33 -0
- package/dist/Toggle.d.ts +33 -0
- package/dist/Toggle.js +397 -0
- package/dist/Toggle.mjs +8 -0
- package/dist/VirtualList.d.mts +19 -0
- package/dist/VirtualList.d.ts +19 -0
- package/dist/VirtualList.js +38 -0
- package/dist/VirtualList.mjs +1 -0
- package/dist/chunk-2CE3TQVY.mjs +11 -0
- package/dist/chunk-2UYENBLV.mjs +49 -0
- package/dist/chunk-3BBOZ3OQ.mjs +41 -0
- package/dist/chunk-5IKW3VNC.mjs +43 -0
- package/dist/chunk-63357L2X.mjs +51 -0
- package/dist/chunk-6LQYY7HC.mjs +127 -0
- package/dist/chunk-6Q64UFIA.mjs +71 -0
- package/dist/chunk-76PFOSM2.mjs +41 -0
- package/dist/chunk-7H2OR44A.mjs +14 -0
- package/dist/chunk-A4MDAP7G.mjs +42 -0
- package/dist/chunk-AU2VDY4P.mjs +190 -0
- package/dist/chunk-BRKYVJVV.mjs +60 -0
- package/dist/chunk-CRYBX2CM.mjs +146 -0
- package/dist/chunk-DITNP6PL.mjs +106 -0
- package/dist/chunk-FTLJOUOQ.mjs +97 -0
- package/dist/chunk-GCWOGZYL.mjs +104 -0
- package/dist/chunk-GNGLDL6Z.mjs +60 -0
- package/dist/chunk-GPOUINK5.mjs +148 -0
- package/dist/chunk-HSPSMN6U.mjs +115 -0
- package/dist/chunk-IRRY3CRZ.mjs +82 -0
- package/dist/chunk-JB67UOB5.mjs +92 -0
- package/dist/chunk-JBLL7U3U.mjs +64 -0
- package/dist/chunk-KWCPOM6W.mjs +136 -0
- package/dist/chunk-KZJRQOIU.mjs +64 -0
- package/dist/chunk-L7E7TVEZ.mjs +145 -0
- package/dist/chunk-LG4DO3DK.mjs +174 -0
- package/dist/chunk-LWG526VX.mjs +139 -0
- package/dist/chunk-MN7OG7IY.mjs +96 -0
- package/dist/chunk-MX6HRKMI.mjs +29 -0
- package/dist/chunk-NC5ZTR2Y.mjs +32 -0
- package/dist/chunk-NQGVLMWG.mjs +90 -0
- package/dist/chunk-QCNARS3X.mjs +46 -0
- package/dist/chunk-QXGYKWI7.mjs +134 -0
- package/dist/chunk-QY3X2UYR.mjs +191 -0
- package/dist/chunk-RKLHUDZS.mjs +92 -0
- package/dist/chunk-RMMK64W5.mjs +54 -0
- package/dist/chunk-RR2VQLKE.mjs +190 -0
- package/dist/chunk-RTC3CFXF.mjs +29 -0
- package/dist/chunk-SBZYEV4S.mjs +61 -0
- package/dist/chunk-SOA2Z4RB.mjs +82 -0
- package/dist/chunk-SOYNZDVY.mjs +151 -0
- package/dist/chunk-T7XZ7H7Y.mjs +57 -0
- package/dist/chunk-TAJ2PQ2O.mjs +163 -0
- package/dist/chunk-U4N7WF4Z.mjs +108 -0
- package/dist/chunk-URDE3EUU.mjs +132 -0
- package/dist/chunk-URLL5JBR.mjs +245 -0
- package/dist/chunk-XDMN67KV.mjs +59 -0
- package/dist/chunk-Y6MXOREN.mjs +120 -0
- package/dist/chunk-YZJAFS4P.mjs +131 -0
- package/dist/index.d.mts +94 -852
- package/dist/index.d.ts +94 -852
- package/dist/index.js +1387 -942
- package/dist/index.mjs +50 -3844
- package/package.json +23 -14
- package/src/assets/fonts/Sohne-Bold.otf +0 -0
- package/src/assets/fonts/Sohne-BoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBold.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLight.otf +0 -0
- package/src/assets/fonts/Sohne-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Italic.otf +0 -0
- package/src/assets/fonts/Sohne-Light.otf +0 -0
- package/src/assets/fonts/Sohne-LightItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Medium.otf +0 -0
- package/src/assets/fonts/Sohne-MediumItalic.otf +0 -0
- package/src/assets/fonts/Sohne-Regular.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBold.otf +0 -0
- package/src/assets/fonts/Sohne-SemiBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Bold.otf +0 -0
- package/src/assets/fonts/SohneMono-BoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBold.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraBoldItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLight.otf +0 -0
- package/src/assets/fonts/SohneMono-ExtraLightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Italic.otf +0 -0
- package/src/assets/fonts/SohneMono-Light.otf +0 -0
- package/src/assets/fonts/SohneMono-LightItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Medium.otf +0 -0
- package/src/assets/fonts/SohneMono-MediumItalic.otf +0 -0
- package/src/assets/fonts/SohneMono-Regular.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBold.otf +0 -0
- package/src/assets/fonts/SohneMono-SemiBoldItalic.otf +0 -0
- package/src/components/Accordion/Accordion.tsx +13 -15
- package/src/components/AlertBanner/AlertBanner.tsx +33 -12
- package/src/components/Avatar/Avatar.tsx +4 -2
- package/src/components/Badge/Badge.tsx +4 -2
- package/src/components/Button/Button.tsx +30 -29
- package/src/components/ButtonGroup/ButtonGroup.tsx +13 -10
- package/src/components/Card/Card.tsx +36 -65
- package/src/components/CategoryStrip/CategoryStrip.tsx +68 -58
- package/src/components/Checkbox/Checkbox.tsx +41 -55
- package/src/components/Chip/Chip.tsx +49 -84
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +2 -2
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +4 -2
- package/src/components/CurrencyInput/CurrencyInput.tsx +2 -2
- package/src/components/DetailRow/DetailRow.tsx +9 -7
- package/src/components/EmptyState/EmptyState.tsx +2 -2
- package/src/components/Form/Form.tsx +149 -0
- package/src/components/Form/index.ts +1 -0
- package/src/components/IconButton/IconButton.tsx +24 -20
- package/src/components/Input/Input.tsx +63 -50
- package/src/components/LabelValue/LabelValue.tsx +6 -4
- package/src/components/ListGroup/ListGroup.tsx +145 -0
- package/src/components/ListGroup/index.ts +1 -0
- package/src/components/ListItem/ListItem.tsx +30 -43
- package/src/components/MediaCard/MediaCard.tsx +31 -29
- package/src/components/MenuGroup/MenuGroup.tsx +145 -0
- package/src/components/MenuGroup/index.ts +1 -0
- package/src/components/MenuItem/MenuItem.tsx +29 -40
- package/src/components/MonthPicker/MonthPicker.tsx +14 -4
- package/src/components/Pressable/Pressable.tsx +27 -46
- package/src/components/Progress/Progress.tsx +21 -12
- package/src/components/RadioGroup/RadioGroup.tsx +55 -32
- package/src/components/Select/Select.tsx +23 -21
- package/src/components/Separator/Separator.tsx +1 -3
- package/src/components/Sheet/Sheet.tsx +85 -18
- package/src/components/Skeleton/Skeleton.tsx +25 -14
- package/src/components/Slider/Slider.tsx +13 -3
- package/src/components/Spinner/Spinner.tsx +1 -1
- package/src/components/Switch/Switch.tsx +70 -52
- package/src/components/Tabs/Tabs.tsx +59 -47
- package/src/components/Text/Text.tsx +3 -1
- package/src/components/Textarea/Textarea.tsx +44 -23
- package/src/components/Toast/Toast.tsx +6 -6
- package/src/components/Toggle/Toggle.tsx +86 -68
- package/src/components/VirtualList/VirtualList.tsx +60 -0
- package/src/components/VirtualList/index.ts +1 -0
- package/src/fonts.ts +38 -20
- package/src/index.ts +5 -1
- package/src/theme/colors.ts +53 -39
- package/src/theme/types.ts +3 -0
- package/src/tokens.ts +49 -39
- package/src/utils/animations.ts +58 -0
- package/src/utils/icons.ts +47 -20
- package/src/utils/useColorTransition.ts +40 -0
- package/src/utils/usePressScale.ts +75 -0
- package/src/assets/fonts/Poppins-Black.ttf +0 -0
- package/src/assets/fonts/Poppins-BlackItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Bold.ttf +0 -0
- package/src/assets/fonts/Poppins-BoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBold.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLight.ttf +0 -0
- package/src/assets/fonts/Poppins-ExtraLightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Italic.ttf +0 -0
- package/src/assets/fonts/Poppins-Light.ttf +0 -0
- package/src/assets/fonts/Poppins-LightItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Medium.ttf +0 -0
- package/src/assets/fonts/Poppins-MediumItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Regular.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBold.ttf +0 -0
- package/src/assets/fonts/Poppins-SemiBoldItalic.ttf +0 -0
- package/src/assets/fonts/Poppins-Thin.ttf +0 -0
- package/src/assets/fonts/Poppins-ThinItalic.ttf +0 -0
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
View,
|
|
4
4
|
Image,
|
|
5
5
|
Text,
|
|
6
6
|
TouchableOpacity,
|
|
7
|
-
Animated,
|
|
8
7
|
StyleSheet,
|
|
9
8
|
ViewStyle,
|
|
10
9
|
ImageSourcePropType,
|
|
11
10
|
Platform,
|
|
12
11
|
} from 'react-native'
|
|
12
|
+
import Animated from 'react-native-reanimated'
|
|
13
13
|
import { impactLight } from '../../utils/haptics'
|
|
14
14
|
import { useTheme } from '../../theme'
|
|
15
15
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
16
16
|
import { renderIcon } from '../../utils/icons'
|
|
17
17
|
import { useHover } from '../../utils/hover'
|
|
18
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
19
|
+
import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
|
|
18
20
|
import { RADIUS, SHADOWS } from '../../tokens'
|
|
19
21
|
|
|
20
|
-
const nativeDriver = Platform.OS !== 'web'
|
|
21
|
-
|
|
22
22
|
export type MediaCardAspectRatio = '1:1' | '4:3' | '16:9' | '4:5' | '3:2'
|
|
23
23
|
|
|
24
24
|
const aspectRatioMap: Record<MediaCardAspectRatio, number> = {
|
|
@@ -57,9 +57,11 @@ export interface MediaCardProps {
|
|
|
57
57
|
imageStyle?: ViewStyle
|
|
58
58
|
/** Additional content rendered below caption. */
|
|
59
59
|
footer?: React.ReactNode
|
|
60
|
+
/** Accessibility label override. Defaults to title (and subtitle if present). */
|
|
61
|
+
accessibilityLabel?: string
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
|
|
64
|
+
function MediaCardBase({
|
|
63
65
|
imageSource,
|
|
64
66
|
aspectRatio = '4:3',
|
|
65
67
|
badge,
|
|
@@ -74,20 +76,16 @@ export function MediaCard({
|
|
|
74
76
|
style,
|
|
75
77
|
imageStyle,
|
|
76
78
|
footer,
|
|
79
|
+
accessibilityLabel,
|
|
77
80
|
}: MediaCardProps) {
|
|
78
81
|
const { colors } = useTheme()
|
|
79
|
-
const scale = useRef(new Animated.Value(1)).current
|
|
80
82
|
const { hovered, hoverHandlers } = useHover()
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
const handlePressOut = () => {
|
|
88
|
-
if (!onPress) return
|
|
89
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, speed: 40, bounciness: 4 }).start()
|
|
90
|
-
}
|
|
83
|
+
const { animatedStyle, onPressIn, onPressOut } = usePressScale({
|
|
84
|
+
pressScale: PRESS_SCALE.card,
|
|
85
|
+
pressInSpring: SPRINGS.surfacePressIn,
|
|
86
|
+
pressOutSpring: SPRINGS.surfacePressOut,
|
|
87
|
+
disabled: !onPress,
|
|
88
|
+
})
|
|
91
89
|
|
|
92
90
|
const handlePress = () => {
|
|
93
91
|
if (!onPress) return
|
|
@@ -102,6 +100,8 @@ export function MediaCard({
|
|
|
102
100
|
? renderIcon(actionIconName, 18, actionActive ? colors.primary : colors.background)
|
|
103
101
|
: actionIcon ?? renderIcon('heart', 18, actionActive ? colors.primary : colors.background)
|
|
104
102
|
|
|
103
|
+
const a11yLabel = accessibilityLabel ?? [title, subtitle].filter(Boolean).join('. ')
|
|
104
|
+
|
|
105
105
|
const cardContent = (
|
|
106
106
|
<View
|
|
107
107
|
style={[
|
|
@@ -111,9 +111,8 @@ export function MediaCard({
|
|
|
111
111
|
]}
|
|
112
112
|
{...(Platform.OS === 'web' ? hoverHandlers : {})}
|
|
113
113
|
>
|
|
114
|
-
{/* Image area */}
|
|
115
114
|
<View style={[styles.imageContainer, imageStyle]}>
|
|
116
|
-
<View style={{ paddingTop: `${ratio * 100}%` as
|
|
115
|
+
<View style={{ paddingTop: `${ratio * 100}%` as `${number}%` }}>
|
|
117
116
|
<View style={StyleSheet.absoluteFill}>
|
|
118
117
|
{imageSource ? (
|
|
119
118
|
<Image
|
|
@@ -127,27 +126,27 @@ export function MediaCard({
|
|
|
127
126
|
</View>
|
|
128
127
|
</View>
|
|
129
128
|
|
|
130
|
-
{/* Badge — top left */}
|
|
131
129
|
{badge && (
|
|
132
130
|
<View style={styles.badgeContainer}>
|
|
133
131
|
{badge}
|
|
134
132
|
</View>
|
|
135
133
|
)}
|
|
136
134
|
|
|
137
|
-
{/* Action icon — top right */}
|
|
138
135
|
{(onActionPress || actionIcon || actionIconName) && (
|
|
139
136
|
<TouchableOpacity
|
|
140
137
|
style={[styles.actionButton, { backgroundColor: 'rgba(0,0,0,0.24)' }]}
|
|
141
138
|
onPress={() => { impactLight(); onActionPress?.() }}
|
|
142
139
|
activeOpacity={0.8}
|
|
143
140
|
touchSoundDisabled={true}
|
|
141
|
+
accessibilityRole="button"
|
|
142
|
+
accessibilityLabel={actionIconName ?? 'action'}
|
|
143
|
+
accessibilityState={{ selected: actionActive }}
|
|
144
144
|
>
|
|
145
145
|
{resolvedActionIcon}
|
|
146
146
|
</TouchableOpacity>
|
|
147
147
|
)}
|
|
148
148
|
</View>
|
|
149
149
|
|
|
150
|
-
{/* Metadata */}
|
|
151
150
|
{(title || subtitle || caption || footer) && (
|
|
152
151
|
<View style={styles.meta}>
|
|
153
152
|
{title ? (
|
|
@@ -173,13 +172,15 @@ export function MediaCard({
|
|
|
173
172
|
|
|
174
173
|
if (onPress) {
|
|
175
174
|
return (
|
|
176
|
-
<Animated.View style={
|
|
175
|
+
<Animated.View style={animatedStyle}>
|
|
177
176
|
<TouchableOpacity
|
|
178
177
|
onPress={handlePress}
|
|
179
|
-
onPressIn={
|
|
180
|
-
onPressOut={
|
|
178
|
+
onPressIn={onPressIn}
|
|
179
|
+
onPressOut={onPressOut}
|
|
181
180
|
activeOpacity={1}
|
|
182
181
|
touchSoundDisabled={true}
|
|
182
|
+
accessibilityRole="button"
|
|
183
|
+
accessibilityLabel={a11yLabel}
|
|
183
184
|
>
|
|
184
185
|
{cardContent}
|
|
185
186
|
</TouchableOpacity>
|
|
@@ -190,14 +191,15 @@ export function MediaCard({
|
|
|
190
191
|
return cardContent
|
|
191
192
|
}
|
|
192
193
|
|
|
194
|
+
export const MediaCard = React.memo(MediaCardBase)
|
|
195
|
+
|
|
193
196
|
const styles = StyleSheet.create({
|
|
194
197
|
card: {
|
|
195
|
-
borderRadius: RADIUS.md,
|
|
198
|
+
borderRadius: RADIUS.md,
|
|
196
199
|
overflow: 'hidden',
|
|
197
200
|
backgroundColor: 'transparent',
|
|
198
201
|
},
|
|
199
202
|
cardHovered: {
|
|
200
|
-
// Web hover: lift shadow
|
|
201
203
|
...SHADOWS.md,
|
|
202
204
|
},
|
|
203
205
|
imageContainer: {
|
|
@@ -233,17 +235,17 @@ const styles = StyleSheet.create({
|
|
|
233
235
|
gap: vs(2),
|
|
234
236
|
},
|
|
235
237
|
title: {
|
|
236
|
-
fontFamily: '
|
|
238
|
+
fontFamily: 'Sohne-SemiBold',
|
|
237
239
|
fontSize: ms(14),
|
|
238
240
|
lineHeight: mvs(20),
|
|
239
241
|
},
|
|
240
242
|
subtitle: {
|
|
241
|
-
fontFamily: '
|
|
243
|
+
fontFamily: 'Sohne-Regular',
|
|
242
244
|
fontSize: ms(13),
|
|
243
245
|
lineHeight: mvs(18),
|
|
244
246
|
},
|
|
245
247
|
caption: {
|
|
246
|
-
fontFamily: '
|
|
248
|
+
fontFamily: 'Sohne-Regular',
|
|
247
249
|
fontSize: ms(12),
|
|
248
250
|
lineHeight: mvs(16),
|
|
249
251
|
},
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { View, Text, StyleSheet, ViewStyle } from 'react-native'
|
|
3
|
+
import { useTheme } from '../../theme'
|
|
4
|
+
import { s, vs } from '../../utils/scaling'
|
|
5
|
+
import { RADIUS } from '../../tokens'
|
|
6
|
+
|
|
7
|
+
export type MenuGroupVariant = 'plain' | 'card'
|
|
8
|
+
|
|
9
|
+
export interface MenuGroupProps {
|
|
10
|
+
children: React.ReactNode
|
|
11
|
+
/**
|
|
12
|
+
* - `plain` (default): no background, plain MenuItems inside.
|
|
13
|
+
* - `card`: card surface with background + border wrapping plain MenuItems.
|
|
14
|
+
*/
|
|
15
|
+
variant?: MenuGroupVariant
|
|
16
|
+
style?: ViewStyle
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface MenuGroupHeaderProps {
|
|
20
|
+
children: React.ReactNode
|
|
21
|
+
style?: ViewStyle
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface MenuGroupFooterProps {
|
|
25
|
+
children: React.ReactNode
|
|
26
|
+
style?: ViewStyle
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* MenuGroup wraps multiple MenuItems and auto-adds separators between them.
|
|
31
|
+
* Use variant="card" for a standalone surface or "plain" for items inside another container.
|
|
32
|
+
*/
|
|
33
|
+
export function MenuGroup({ children, variant = 'plain', style }: MenuGroupProps) {
|
|
34
|
+
const { colors } = useTheme()
|
|
35
|
+
|
|
36
|
+
// Auto-inject showSeparator={true} to all MenuItem children except the last
|
|
37
|
+
const processedChildren = React.Children.map(children, (child, index) => {
|
|
38
|
+
if (!React.isValidElement(child)) return child
|
|
39
|
+
|
|
40
|
+
// Skip MenuGroup.Header and MenuGroup.Footer
|
|
41
|
+
if (child.type === MenuGroupHeader || child.type === MenuGroupFooter) {
|
|
42
|
+
return child
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if it's a MenuItem (has onPress prop as a heuristic)
|
|
46
|
+
const childProps = child.props as Record<string, unknown>
|
|
47
|
+
const isMenuItem = 'onPress' in childProps
|
|
48
|
+
if (!isMenuItem) return child
|
|
49
|
+
|
|
50
|
+
const isLast = index === React.Children.count(children) - 1
|
|
51
|
+
|
|
52
|
+
// Only add separator if not already explicitly set and not last item
|
|
53
|
+
if (childProps['showSeparator'] === undefined && !isLast) {
|
|
54
|
+
return React.cloneElement(child as React.ReactElement<Record<string, unknown>>, {
|
|
55
|
+
showSeparator: true,
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return child
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const cardStyle: ViewStyle =
|
|
63
|
+
variant === 'card'
|
|
64
|
+
? {
|
|
65
|
+
backgroundColor: colors.card,
|
|
66
|
+
borderRadius: RADIUS.md,
|
|
67
|
+
borderWidth: 1,
|
|
68
|
+
borderColor: colors.border,
|
|
69
|
+
shadowColor: '#000',
|
|
70
|
+
shadowOffset: { width: 0, height: 2 },
|
|
71
|
+
shadowOpacity: 0.06,
|
|
72
|
+
shadowRadius: 6,
|
|
73
|
+
elevation: 2,
|
|
74
|
+
paddingVertical: vs(4),
|
|
75
|
+
}
|
|
76
|
+
: {}
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
<View style={[styles.container, cardStyle, style]}>
|
|
80
|
+
{processedChildren}
|
|
81
|
+
</View>
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function MenuGroupHeader({ children, style }: MenuGroupHeaderProps) {
|
|
86
|
+
const { colors } = useTheme()
|
|
87
|
+
|
|
88
|
+
if (typeof children === 'string') {
|
|
89
|
+
return (
|
|
90
|
+
<View style={[styles.header, { borderBottomColor: colors.separator }, style]}>
|
|
91
|
+
<Text style={[styles.headerText, { color: colors.foregroundMuted }]} allowFontScaling={true}>
|
|
92
|
+
{children}
|
|
93
|
+
</Text>
|
|
94
|
+
</View>
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return <View style={[styles.header, { borderBottomColor: colors.separator }, style]}>{children}</View>
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function MenuGroupFooter({ children, style }: MenuGroupFooterProps) {
|
|
102
|
+
const { colors } = useTheme()
|
|
103
|
+
|
|
104
|
+
if (typeof children === 'string') {
|
|
105
|
+
return (
|
|
106
|
+
<View style={[styles.footer, style]}>
|
|
107
|
+
<Text style={[styles.footerText, { color: colors.foregroundMuted }]} allowFontScaling={true}>
|
|
108
|
+
{children}
|
|
109
|
+
</Text>
|
|
110
|
+
</View>
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return <View style={[styles.footer, style]}>{children}</View>
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
MenuGroup.Header = MenuGroupHeader
|
|
118
|
+
MenuGroup.Footer = MenuGroupFooter
|
|
119
|
+
|
|
120
|
+
const styles = StyleSheet.create({
|
|
121
|
+
container: {
|
|
122
|
+
overflow: 'hidden',
|
|
123
|
+
},
|
|
124
|
+
header: {
|
|
125
|
+
paddingHorizontal: s(16),
|
|
126
|
+
paddingTop: vs(12),
|
|
127
|
+
paddingBottom: vs(8),
|
|
128
|
+
borderBottomWidth: StyleSheet.hairlineWidth,
|
|
129
|
+
},
|
|
130
|
+
headerText: {
|
|
131
|
+
fontFamily: 'Sohne-SemiBold',
|
|
132
|
+
fontSize: 13,
|
|
133
|
+
letterSpacing: 0.32,
|
|
134
|
+
textTransform: 'uppercase',
|
|
135
|
+
},
|
|
136
|
+
footer: {
|
|
137
|
+
paddingHorizontal: s(16),
|
|
138
|
+
paddingTop: vs(8),
|
|
139
|
+
paddingBottom: vs(12),
|
|
140
|
+
},
|
|
141
|
+
footerText: {
|
|
142
|
+
fontFamily: 'Sohne-Regular',
|
|
143
|
+
fontSize: 12,
|
|
144
|
+
},
|
|
145
|
+
})
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MenuGroup'
|
|
@@ -1,22 +1,21 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import {
|
|
3
3
|
TouchableOpacity,
|
|
4
|
-
Animated,
|
|
5
4
|
View,
|
|
6
5
|
Text,
|
|
7
6
|
StyleSheet,
|
|
8
7
|
ViewStyle,
|
|
9
8
|
TextStyle,
|
|
10
|
-
Platform,
|
|
11
9
|
} from 'react-native'
|
|
10
|
+
import Animated from 'react-native-reanimated'
|
|
12
11
|
import { Entypo } from '@expo/vector-icons'
|
|
13
12
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
14
13
|
import { useTheme } from '../../theme'
|
|
15
14
|
import { s, vs, ms } from '../../utils/scaling'
|
|
16
15
|
import { renderIcon } from '../../utils/icons'
|
|
17
16
|
import { RADIUS } from '../../tokens'
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
18
|
+
import { SPRINGS, PRESS_SCALE } from '../../utils/animations'
|
|
20
19
|
|
|
21
20
|
export type MenuItemVariant = 'plain' | 'card'
|
|
22
21
|
|
|
@@ -57,9 +56,11 @@ export interface MenuItemProps {
|
|
|
57
56
|
style?: ViewStyle
|
|
58
57
|
/** Style applied to the label Text. */
|
|
59
58
|
labelStyle?: TextStyle
|
|
59
|
+
/** Accessibility label override. Defaults to label. */
|
|
60
|
+
accessibilityLabel?: string
|
|
60
61
|
}
|
|
61
62
|
|
|
62
|
-
|
|
63
|
+
function MenuItemBase({
|
|
63
64
|
label,
|
|
64
65
|
subtitle,
|
|
65
66
|
iconName,
|
|
@@ -73,30 +74,15 @@ export function MenuItem({
|
|
|
73
74
|
showSeparator = false,
|
|
74
75
|
style,
|
|
75
76
|
labelStyle,
|
|
77
|
+
accessibilityLabel,
|
|
76
78
|
}: MenuItemProps) {
|
|
77
79
|
const { colors } = useTheme()
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
useNativeDriver: nativeDriver,
|
|
85
|
-
stiffness: 350,
|
|
86
|
-
damping: 28,
|
|
87
|
-
mass: 0.9,
|
|
88
|
-
}).start()
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const handlePressOut = () => {
|
|
92
|
-
Animated.spring(scale, {
|
|
93
|
-
toValue: 1,
|
|
94
|
-
useNativeDriver: nativeDriver,
|
|
95
|
-
stiffness: 220,
|
|
96
|
-
damping: 20,
|
|
97
|
-
mass: 0.9,
|
|
98
|
-
}).start()
|
|
99
|
-
}
|
|
80
|
+
const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
81
|
+
pressScale: PRESS_SCALE.row,
|
|
82
|
+
pressInSpring: SPRINGS.surfacePressIn,
|
|
83
|
+
pressOutSpring: SPRINGS.surfacePressOut,
|
|
84
|
+
disabled,
|
|
85
|
+
})
|
|
100
86
|
|
|
101
87
|
const handlePress = () => {
|
|
102
88
|
hapticSelection()
|
|
@@ -122,16 +108,21 @@ export function MenuItem({
|
|
|
122
108
|
}
|
|
123
109
|
: {}
|
|
124
110
|
|
|
111
|
+
const a11yLabel = accessibilityLabel ?? (subtitle ? `${label}. ${subtitle}` : label)
|
|
112
|
+
|
|
125
113
|
return (
|
|
126
|
-
<Animated.View style={[
|
|
114
|
+
<Animated.View style={[animatedStyle, disabled && styles.disabled]} {...hoverHandlers}>
|
|
127
115
|
<TouchableOpacity
|
|
128
116
|
style={[styles.container, cardStyle, style]}
|
|
129
117
|
onPress={handlePress}
|
|
130
|
-
onPressIn={
|
|
131
|
-
onPressOut={
|
|
118
|
+
onPressIn={onPressIn}
|
|
119
|
+
onPressOut={onPressOut}
|
|
132
120
|
disabled={disabled}
|
|
133
121
|
activeOpacity={1}
|
|
134
122
|
touchSoundDisabled={true}
|
|
123
|
+
accessibilityRole="button"
|
|
124
|
+
accessibilityLabel={a11yLabel}
|
|
125
|
+
accessibilityState={{ disabled }}
|
|
135
126
|
>
|
|
136
127
|
{resolvedIcon ? (
|
|
137
128
|
<View style={styles.iconContainer}>{resolvedIcon}</View>
|
|
@@ -157,7 +148,7 @@ export function MenuItem({
|
|
|
157
148
|
</View>
|
|
158
149
|
|
|
159
150
|
{rightRender !== undefined ? (
|
|
160
|
-
<View
|
|
151
|
+
<View
|
|
161
152
|
style={styles.rightContainer}
|
|
162
153
|
onStartShouldSetResponder={() => true}
|
|
163
154
|
onResponderRelease={() => {}}
|
|
@@ -173,11 +164,7 @@ export function MenuItem({
|
|
|
173
164
|
<View
|
|
174
165
|
style={[
|
|
175
166
|
styles.separator,
|
|
176
|
-
{
|
|
177
|
-
backgroundColor: colors.border,
|
|
178
|
-
marginLeft: resolvedIcon ? s(22) + s(12) : 0,
|
|
179
|
-
opacity: 0.6,
|
|
180
|
-
},
|
|
167
|
+
{ backgroundColor: colors.separator },
|
|
181
168
|
]}
|
|
182
169
|
/>
|
|
183
170
|
) : null}
|
|
@@ -185,11 +172,13 @@ export function MenuItem({
|
|
|
185
172
|
)
|
|
186
173
|
}
|
|
187
174
|
|
|
175
|
+
export const MenuItem = React.memo(MenuItemBase)
|
|
176
|
+
|
|
188
177
|
const styles = StyleSheet.create({
|
|
189
178
|
container: {
|
|
190
179
|
flexDirection: 'row',
|
|
191
180
|
alignItems: 'center',
|
|
192
|
-
paddingHorizontal:
|
|
181
|
+
paddingHorizontal: s(16),
|
|
193
182
|
paddingVertical: vs(16),
|
|
194
183
|
minHeight: vs(54),
|
|
195
184
|
gap: s(12),
|
|
@@ -205,11 +194,11 @@ const styles = StyleSheet.create({
|
|
|
205
194
|
justifyContent: 'center',
|
|
206
195
|
},
|
|
207
196
|
label: {
|
|
208
|
-
fontFamily: '
|
|
197
|
+
fontFamily: 'Sohne-Medium',
|
|
209
198
|
fontSize: ms(15),
|
|
210
199
|
},
|
|
211
200
|
subtitle: {
|
|
212
|
-
fontFamily: '
|
|
201
|
+
fontFamily: 'Sohne-Regular',
|
|
213
202
|
fontSize: ms(12),
|
|
214
203
|
marginTop: vs(1),
|
|
215
204
|
},
|
|
@@ -3,7 +3,7 @@ import { View, Text, TouchableOpacity, StyleSheet, ViewStyle } from 'react-nativ
|
|
|
3
3
|
import { Entypo } from '@expo/vector-icons'
|
|
4
4
|
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
5
5
|
import { useTheme } from '../../theme'
|
|
6
|
-
import { s,
|
|
6
|
+
import { s, ms, mvs } from '../../utils/scaling'
|
|
7
7
|
|
|
8
8
|
const MONTH_NAMES: Record<string, string[]> = {
|
|
9
9
|
en: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
|
|
@@ -56,16 +56,23 @@ export function MonthPicker({ value, onChange, locale = 'en', formatLabel, style
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
return (
|
|
59
|
-
<View style={[styles.container, style]}>
|
|
59
|
+
<View style={[styles.container, style]} accessibilityRole="adjustable" accessibilityLabel={getLabel()}>
|
|
60
60
|
<TouchableOpacity
|
|
61
61
|
style={styles.arrow}
|
|
62
62
|
onPress={handlePrev}
|
|
63
63
|
activeOpacity={0.6}
|
|
64
64
|
touchSoundDisabled={true}
|
|
65
|
+
accessibilityRole="button"
|
|
66
|
+
accessibilityLabel="Previous month"
|
|
67
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
65
68
|
>
|
|
66
69
|
<Entypo name="chevron-left" size={22} color={colors.foreground} />
|
|
67
70
|
</TouchableOpacity>
|
|
68
|
-
<Text
|
|
71
|
+
<Text
|
|
72
|
+
style={[styles.label, { color: colors.foreground }]}
|
|
73
|
+
allowFontScaling={true}
|
|
74
|
+
accessibilityLiveRegion="polite"
|
|
75
|
+
>
|
|
69
76
|
{getLabel()}
|
|
70
77
|
</Text>
|
|
71
78
|
<TouchableOpacity
|
|
@@ -73,6 +80,9 @@ export function MonthPicker({ value, onChange, locale = 'en', formatLabel, style
|
|
|
73
80
|
onPress={handleNext}
|
|
74
81
|
activeOpacity={0.6}
|
|
75
82
|
touchSoundDisabled={true}
|
|
83
|
+
accessibilityRole="button"
|
|
84
|
+
accessibilityLabel="Next month"
|
|
85
|
+
hitSlop={{ top: 8, bottom: 8, left: 8, right: 8 }}
|
|
76
86
|
>
|
|
77
87
|
<Entypo name="chevron-right" size={22} color={colors.foreground} />
|
|
78
88
|
</TouchableOpacity>
|
|
@@ -93,7 +103,7 @@ const styles = StyleSheet.create({
|
|
|
93
103
|
justifyContent: 'center',
|
|
94
104
|
},
|
|
95
105
|
label: {
|
|
96
|
-
fontFamily: '
|
|
106
|
+
fontFamily: 'Sohne-Medium',
|
|
97
107
|
fontSize: ms(17),
|
|
98
108
|
lineHeight: mvs(24),
|
|
99
109
|
textAlign: 'center',
|
|
@@ -1,15 +1,9 @@
|
|
|
1
|
-
import React
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
Animated,
|
|
5
|
-
ViewStyle,
|
|
6
|
-
Platform,
|
|
7
|
-
TouchableOpacityProps,
|
|
8
|
-
} from 'react-native'
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { TouchableOpacity, Platform, ViewStyle, TouchableOpacityProps } from 'react-native'
|
|
3
|
+
import Animated from 'react-native-reanimated'
|
|
9
4
|
import { impactLight } from '../../utils/haptics'
|
|
10
|
-
import {
|
|
11
|
-
|
|
12
|
-
const nativeDriver = Platform.OS !== 'web'
|
|
5
|
+
import { usePressScale } from '../../utils/usePressScale'
|
|
6
|
+
import { PRESS_SCALE, SPRINGS } from '../../utils/animations'
|
|
13
7
|
|
|
14
8
|
export interface PressableProps extends Omit<TouchableOpacityProps, 'activeOpacity'> {
|
|
15
9
|
/** Children content to render inside the pressable. */
|
|
@@ -18,7 +12,10 @@ export interface PressableProps extends Omit<TouchableOpacityProps, 'activeOpaci
|
|
|
18
12
|
onPress?: () => void
|
|
19
13
|
/** Scale value on press. Defaults to `0.98` (MediaCard-style). */
|
|
20
14
|
pressScale?: number
|
|
21
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* @deprecated Use Reanimated spring config via `pressOutSpring` instead. Ignored.
|
|
17
|
+
* Kept for backwards compatibility with v6.x consumers.
|
|
18
|
+
*/
|
|
22
19
|
bounciness?: number
|
|
23
20
|
/** Enable haptic feedback on press. Defaults to `true`. */
|
|
24
21
|
haptics?: boolean
|
|
@@ -31,42 +28,29 @@ export interface PressableProps extends Omit<TouchableOpacityProps, 'activeOpaci
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
/**
|
|
34
|
-
* Generic pressable with
|
|
35
|
-
*
|
|
31
|
+
* Generic pressable with a calibrated spring bounce — Apple HIG / Airbnb feel.
|
|
32
|
+
* All animation runs on the UI thread via Reanimated v4 worklets.
|
|
33
|
+
*
|
|
34
|
+
* Use this for any custom pressable surface that needs consistent press feel
|
|
35
|
+
* (cards, list rows, image tiles, etc).
|
|
36
36
|
*/
|
|
37
37
|
export function Pressable({
|
|
38
38
|
children,
|
|
39
39
|
onPress,
|
|
40
|
-
pressScale =
|
|
41
|
-
bounciness = 4,
|
|
40
|
+
pressScale = PRESS_SCALE.card,
|
|
42
41
|
haptics = true,
|
|
43
42
|
style,
|
|
44
43
|
disabled,
|
|
45
44
|
hoverScale = 1.02,
|
|
46
45
|
...touchableProps
|
|
47
46
|
}: PressableProps) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
useNativeDriver: nativeDriver,
|
|
56
|
-
speed: 40,
|
|
57
|
-
bounciness: 0,
|
|
58
|
-
}).start()
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const handlePressOut = () => {
|
|
62
|
-
if (disabled) return
|
|
63
|
-
Animated.spring(scale, {
|
|
64
|
-
toValue: 1,
|
|
65
|
-
useNativeDriver: nativeDriver,
|
|
66
|
-
speed: 40,
|
|
67
|
-
bounciness,
|
|
68
|
-
}).start()
|
|
69
|
-
}
|
|
47
|
+
const { animatedStyle, onPressIn, onPressOut, hoverHandlers } = usePressScale({
|
|
48
|
+
pressScale,
|
|
49
|
+
hoverScale,
|
|
50
|
+
pressInSpring: SPRINGS.surfacePressIn,
|
|
51
|
+
pressOutSpring: SPRINGS.surfacePressOut,
|
|
52
|
+
disabled,
|
|
53
|
+
})
|
|
70
54
|
|
|
71
55
|
const handlePress = () => {
|
|
72
56
|
if (disabled || !onPress) return
|
|
@@ -74,23 +58,20 @@ export function Pressable({
|
|
|
74
58
|
onPress()
|
|
75
59
|
}
|
|
76
60
|
|
|
77
|
-
const hoverScaleValue = hovered && hoverScale !== 1 ? hoverScale : 1
|
|
78
|
-
|
|
79
61
|
return (
|
|
80
62
|
<Animated.View
|
|
81
|
-
style={[
|
|
82
|
-
{ transform: [{ scale: Animated.multiply(scale, hoverScaleValue) }] },
|
|
83
|
-
style,
|
|
84
|
-
]}
|
|
63
|
+
style={[animatedStyle, style]}
|
|
85
64
|
{...(Platform.OS === 'web' ? hoverHandlers : {})}
|
|
86
65
|
>
|
|
87
66
|
<TouchableOpacity
|
|
88
67
|
onPress={handlePress}
|
|
89
|
-
onPressIn={
|
|
90
|
-
onPressOut={
|
|
68
|
+
onPressIn={onPressIn}
|
|
69
|
+
onPressOut={onPressOut}
|
|
91
70
|
activeOpacity={1}
|
|
92
71
|
disabled={disabled}
|
|
93
72
|
touchSoundDisabled={true}
|
|
73
|
+
accessibilityRole="button"
|
|
74
|
+
accessibilityState={{ disabled: !!disabled }}
|
|
94
75
|
{...touchableProps}
|
|
95
76
|
>
|
|
96
77
|
{children}
|