@retray-dev/ui-kit 5.4.0 → 6.1.0
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 +262 -126
- package/dist/index.d.mts +152 -46
- package/dist/index.d.ts +152 -46
- package/dist/index.js +448 -307
- package/dist/index.mjs +438 -305
- package/package.json +7 -2
- package/src/components/Accordion/Accordion.tsx +6 -2
- package/src/components/AlertBanner/AlertBanner.tsx +16 -33
- package/src/components/Button/Button.tsx +18 -6
- package/src/components/Card/Card.tsx +12 -9
- package/src/components/ConfirmDialog/ConfirmDialog.tsx +4 -4
- package/src/components/CurrencyDisplay/CurrencyDisplay.tsx +14 -3
- package/src/components/DetailRow/DetailRow.tsx +13 -8
- package/src/components/EmptyState/EmptyState.tsx +21 -6
- package/src/components/Input/Input.tsx +21 -10
- package/src/components/ListItem/ListItem.tsx +14 -8
- package/src/components/MediaCard/MediaCard.tsx +1 -0
- package/src/components/MenuItem/MenuItem.tsx +228 -0
- package/src/components/MenuItem/index.ts +2 -0
- package/src/components/Select/Select.tsx +1 -1
- package/src/components/Separator/Separator.tsx +2 -0
- package/src/components/Sheet/Sheet.tsx +164 -51
- package/src/components/Sheet/index.ts +1 -1
- package/src/components/Tabs/Tabs.tsx +4 -4
- package/src/components/Toast/Toast.tsx +41 -267
- package/src/components/Toast/index.ts +1 -2
- package/src/components/Toggle/Toggle.tsx +2 -2
- package/src/index.ts +1 -0
- package/src/theme/colors.ts +3 -0
- package/src/theme/types.ts +11 -0
- package/src/tokens.ts +3 -3
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import React, { useRef } from 'react'
|
|
2
|
+
import {
|
|
3
|
+
TouchableOpacity,
|
|
4
|
+
Animated,
|
|
5
|
+
View,
|
|
6
|
+
Text,
|
|
7
|
+
StyleSheet,
|
|
8
|
+
ViewStyle,
|
|
9
|
+
TextStyle,
|
|
10
|
+
Platform,
|
|
11
|
+
} from 'react-native'
|
|
12
|
+
import { Entypo } from '@expo/vector-icons'
|
|
13
|
+
import { selectionAsync as hapticSelection } from '../../utils/haptics'
|
|
14
|
+
import { useTheme } from '../../theme'
|
|
15
|
+
import { s, vs, ms } from '../../utils/scaling'
|
|
16
|
+
import { renderIcon } from '../../utils/icons'
|
|
17
|
+
import { RADIUS } from '../../tokens'
|
|
18
|
+
|
|
19
|
+
const nativeDriver = Platform.OS !== 'web'
|
|
20
|
+
|
|
21
|
+
export type MenuItemVariant = 'plain' | 'card'
|
|
22
|
+
|
|
23
|
+
export interface MenuItemProps {
|
|
24
|
+
label: string
|
|
25
|
+
/** Secondary text rendered below the label. */
|
|
26
|
+
subtitle?: string
|
|
27
|
+
/**
|
|
28
|
+
* Icon name from `@expo/vector-icons` rendered on the left.
|
|
29
|
+
* See https://icons.expo.fyi.
|
|
30
|
+
*/
|
|
31
|
+
iconName?: string
|
|
32
|
+
/** Custom icon node rendered on the left. */
|
|
33
|
+
icon?: React.ReactNode
|
|
34
|
+
/** Override icon color. Defaults to `foreground`. */
|
|
35
|
+
iconColor?: string
|
|
36
|
+
/**
|
|
37
|
+
* Custom content rendered on the right.
|
|
38
|
+
* When provided, replaces the default chevron.
|
|
39
|
+
* Use for checkboxes, switches, badges, or other controls.
|
|
40
|
+
*/
|
|
41
|
+
rightRender?: React.ReactNode
|
|
42
|
+
/**
|
|
43
|
+
* Show chevron on the right. Defaults to `true`.
|
|
44
|
+
* Ignored when `rightRender` is provided.
|
|
45
|
+
*/
|
|
46
|
+
showChevron?: boolean
|
|
47
|
+
onPress: () => void
|
|
48
|
+
disabled?: boolean
|
|
49
|
+
/**
|
|
50
|
+
* - `plain` (default): no background — sits inside a parent surface.
|
|
51
|
+
* - `card`: standalone surface with background + border.
|
|
52
|
+
*/
|
|
53
|
+
variant?: MenuItemVariant
|
|
54
|
+
/** Visual separator line at the bottom. */
|
|
55
|
+
showSeparator?: boolean
|
|
56
|
+
/** Style applied to the outer container. */
|
|
57
|
+
style?: ViewStyle
|
|
58
|
+
/** Style applied to the label Text. */
|
|
59
|
+
labelStyle?: TextStyle
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export function MenuItem({
|
|
63
|
+
label,
|
|
64
|
+
subtitle,
|
|
65
|
+
iconName,
|
|
66
|
+
icon,
|
|
67
|
+
iconColor,
|
|
68
|
+
rightRender,
|
|
69
|
+
showChevron = true,
|
|
70
|
+
onPress,
|
|
71
|
+
disabled = false,
|
|
72
|
+
variant = 'plain',
|
|
73
|
+
showSeparator = false,
|
|
74
|
+
style,
|
|
75
|
+
labelStyle,
|
|
76
|
+
}: MenuItemProps) {
|
|
77
|
+
const { colors } = useTheme()
|
|
78
|
+
const scale = useRef(new Animated.Value(1)).current
|
|
79
|
+
|
|
80
|
+
const handlePressIn = () => {
|
|
81
|
+
if (disabled) return
|
|
82
|
+
Animated.spring(scale, {
|
|
83
|
+
toValue: 0.97,
|
|
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
|
+
}
|
|
100
|
+
|
|
101
|
+
const handlePress = () => {
|
|
102
|
+
hapticSelection()
|
|
103
|
+
onPress()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const resolvedIcon: React.ReactNode = iconName
|
|
107
|
+
? renderIcon(iconName, 22, iconColor ?? colors.foreground)
|
|
108
|
+
: icon
|
|
109
|
+
|
|
110
|
+
const cardStyle: ViewStyle =
|
|
111
|
+
variant === 'card'
|
|
112
|
+
? {
|
|
113
|
+
backgroundColor: colors.card,
|
|
114
|
+
borderRadius: RADIUS.md,
|
|
115
|
+
borderWidth: 1,
|
|
116
|
+
borderColor: colors.border,
|
|
117
|
+
shadowColor: '#000',
|
|
118
|
+
shadowOffset: { width: 0, height: 2 },
|
|
119
|
+
shadowOpacity: 0.06,
|
|
120
|
+
shadowRadius: 6,
|
|
121
|
+
elevation: 2,
|
|
122
|
+
}
|
|
123
|
+
: {}
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<Animated.View style={[{ transform: [{ scale }] }, disabled && styles.disabled]}>
|
|
127
|
+
<TouchableOpacity
|
|
128
|
+
style={[styles.container, cardStyle, style]}
|
|
129
|
+
onPress={handlePress}
|
|
130
|
+
onPressIn={handlePressIn}
|
|
131
|
+
onPressOut={handlePressOut}
|
|
132
|
+
disabled={disabled}
|
|
133
|
+
activeOpacity={1}
|
|
134
|
+
touchSoundDisabled={true}
|
|
135
|
+
>
|
|
136
|
+
{resolvedIcon ? (
|
|
137
|
+
<View style={styles.iconContainer}>{resolvedIcon}</View>
|
|
138
|
+
) : null}
|
|
139
|
+
|
|
140
|
+
<View style={styles.labelContainer}>
|
|
141
|
+
<Text
|
|
142
|
+
style={[styles.label, { color: colors.foreground }, labelStyle]}
|
|
143
|
+
numberOfLines={1}
|
|
144
|
+
allowFontScaling={true}
|
|
145
|
+
>
|
|
146
|
+
{label}
|
|
147
|
+
</Text>
|
|
148
|
+
{subtitle ? (
|
|
149
|
+
<Text
|
|
150
|
+
style={[styles.subtitle, { color: colors.foregroundMuted }]}
|
|
151
|
+
numberOfLines={1}
|
|
152
|
+
allowFontScaling={true}
|
|
153
|
+
>
|
|
154
|
+
{subtitle}
|
|
155
|
+
</Text>
|
|
156
|
+
) : null}
|
|
157
|
+
</View>
|
|
158
|
+
|
|
159
|
+
{rightRender !== undefined ? (
|
|
160
|
+
<View
|
|
161
|
+
style={styles.rightContainer}
|
|
162
|
+
onStartShouldSetResponder={() => true}
|
|
163
|
+
onResponderRelease={() => {}}
|
|
164
|
+
>
|
|
165
|
+
{rightRender}
|
|
166
|
+
</View>
|
|
167
|
+
) : showChevron ? (
|
|
168
|
+
<Entypo name="chevron-right" size={18} color={colors.foregroundMuted} />
|
|
169
|
+
) : null}
|
|
170
|
+
</TouchableOpacity>
|
|
171
|
+
|
|
172
|
+
{showSeparator ? (
|
|
173
|
+
<View
|
|
174
|
+
style={[
|
|
175
|
+
styles.separator,
|
|
176
|
+
{
|
|
177
|
+
backgroundColor: colors.border,
|
|
178
|
+
marginLeft: resolvedIcon ? s(22) + s(12) : 0,
|
|
179
|
+
opacity: 0.6,
|
|
180
|
+
},
|
|
181
|
+
]}
|
|
182
|
+
/>
|
|
183
|
+
) : null}
|
|
184
|
+
</Animated.View>
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const styles = StyleSheet.create({
|
|
189
|
+
container: {
|
|
190
|
+
flexDirection: 'row',
|
|
191
|
+
alignItems: 'center',
|
|
192
|
+
paddingHorizontal: 0,
|
|
193
|
+
paddingVertical: vs(16),
|
|
194
|
+
minHeight: vs(54),
|
|
195
|
+
gap: s(12),
|
|
196
|
+
},
|
|
197
|
+
iconContainer: {
|
|
198
|
+
width: s(22),
|
|
199
|
+
alignItems: 'center',
|
|
200
|
+
justifyContent: 'center',
|
|
201
|
+
flexShrink: 0,
|
|
202
|
+
},
|
|
203
|
+
labelContainer: {
|
|
204
|
+
flex: 1,
|
|
205
|
+
justifyContent: 'center',
|
|
206
|
+
},
|
|
207
|
+
label: {
|
|
208
|
+
fontFamily: 'Poppins-Medium',
|
|
209
|
+
fontSize: ms(15),
|
|
210
|
+
},
|
|
211
|
+
subtitle: {
|
|
212
|
+
fontFamily: 'Poppins-Regular',
|
|
213
|
+
fontSize: ms(12),
|
|
214
|
+
marginTop: vs(1),
|
|
215
|
+
},
|
|
216
|
+
rightContainer: {
|
|
217
|
+
alignItems: 'flex-end',
|
|
218
|
+
justifyContent: 'center',
|
|
219
|
+
flexShrink: 0,
|
|
220
|
+
},
|
|
221
|
+
separator: {
|
|
222
|
+
height: StyleSheet.hairlineWidth,
|
|
223
|
+
marginRight: 0,
|
|
224
|
+
},
|
|
225
|
+
disabled: {
|
|
226
|
+
opacity: 0.45,
|
|
227
|
+
},
|
|
228
|
+
})
|
|
@@ -109,7 +109,7 @@ export function Select({
|
|
|
109
109
|
>
|
|
110
110
|
{selected?.label ?? placeholder}
|
|
111
111
|
</Text>
|
|
112
|
-
<Entypo name="chevron-
|
|
112
|
+
<Entypo name="chevron-down" size={20} color={colors.foregroundMuted} />
|
|
113
113
|
</TouchableOpacity>
|
|
114
114
|
</Animated.View>
|
|
115
115
|
) : null}
|
|
@@ -1,118 +1,214 @@
|
|
|
1
|
-
import React, { useEffect, useRef } from 'react'
|
|
2
|
-
import { View, Text, StyleSheet, ViewStyle,
|
|
1
|
+
import React, { useCallback, useEffect, useRef } from 'react'
|
|
2
|
+
import { View, Text, TouchableOpacity, StyleSheet, ViewStyle, Dimensions, Platform } from 'react-native'
|
|
3
3
|
import {
|
|
4
4
|
BottomSheetModal,
|
|
5
5
|
BottomSheetView,
|
|
6
6
|
BottomSheetScrollView,
|
|
7
7
|
BottomSheetBackdrop,
|
|
8
8
|
BottomSheetModalProvider,
|
|
9
|
+
BottomSheetTextInput,
|
|
10
|
+
BottomSheetFooter,
|
|
9
11
|
type BottomSheetBackdropProps,
|
|
12
|
+
type BottomSheetFooterProps,
|
|
10
13
|
} from '@gorhom/bottom-sheet'
|
|
11
|
-
import {
|
|
14
|
+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
15
|
+
import { AntDesign } from '@expo/vector-icons'
|
|
16
|
+
import { impactMedium } from '../../utils/haptics'
|
|
12
17
|
import { useTheme } from '../../theme'
|
|
13
18
|
import { s, vs, ms, mvs } from '../../utils/scaling'
|
|
14
19
|
|
|
20
|
+
const SCREEN_HEIGHT = Dimensions.get('window').height
|
|
21
|
+
const DEFAULT_MAX_HEIGHT = SCREEN_HEIGHT * 0.85
|
|
22
|
+
const isAndroid = Platform.OS === 'android'
|
|
23
|
+
|
|
15
24
|
export { BottomSheetModalProvider }
|
|
25
|
+
// Re-export BottomSheetTextInput as SheetTextInput for consumer convenience
|
|
26
|
+
export { BottomSheetTextInput as SheetTextInput }
|
|
16
27
|
|
|
17
28
|
export interface SheetProps {
|
|
18
29
|
open: boolean
|
|
19
30
|
onClose: () => void
|
|
20
31
|
title?: string
|
|
32
|
+
/** Secondary text below title. */
|
|
33
|
+
subtitle?: string
|
|
34
|
+
/** @deprecated Use `subtitle` instead. */
|
|
21
35
|
description?: string
|
|
36
|
+
/** Show an X close button in the header. */
|
|
37
|
+
showCloseButton?: boolean
|
|
22
38
|
children?: React.ReactNode
|
|
23
39
|
/** Style for the inner content container. */
|
|
24
40
|
style?: ViewStyle
|
|
25
|
-
/**
|
|
41
|
+
/** Style for the content wrapper (outside the scroll container). */
|
|
42
|
+
contentStyle?: ViewStyle
|
|
43
|
+
/** Render children inside BottomSheetScrollView. */
|
|
26
44
|
scrollable?: boolean
|
|
27
45
|
/** Cap sheet height (dp). Children scroll when content exceeds this value. */
|
|
28
46
|
maxHeight?: number
|
|
29
|
-
/**
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
47
|
+
/**
|
|
48
|
+
* Keyboard behavior — how the sheet responds to keyboard appearance.
|
|
49
|
+
* - 'interactive': offset sheet by keyboard size (default, works on both platforms)
|
|
50
|
+
* - 'fillParent': extend sheet to fill parent view (can cause restore issues with dynamic sizing)
|
|
51
|
+
* - 'extend': extend sheet to maximum snap point
|
|
52
|
+
*
|
|
53
|
+
* Default: 'interactive' on both platforms.
|
|
54
|
+
*/
|
|
55
|
+
keyboardBehavior?: 'extend' | 'fillParent' | 'interactive'
|
|
56
|
+
/**
|
|
57
|
+
* Keyboard blur behavior — what happens when keyboard dismisses.
|
|
58
|
+
* - 'none': do nothing
|
|
59
|
+
* - 'restore': restore sheet to previous position (default)
|
|
60
|
+
*/
|
|
61
|
+
keyboardBlurBehavior?: 'none' | 'restore'
|
|
62
|
+
/**
|
|
63
|
+
* Blur keyboard when user starts dragging the sheet down.
|
|
64
|
+
* Default: true (recommended for better UX)
|
|
65
|
+
*/
|
|
66
|
+
enableBlurKeyboardOnGesture?: boolean
|
|
67
|
+
/**
|
|
68
|
+
* Android-only: defines keyboard input mode.
|
|
69
|
+
* - 'adjustPan': pan the sheet content (default, fixes restore issues with dynamic sizing)
|
|
70
|
+
* - 'adjustResize': resize the sheet container (can cause transparent gap on dismiss)
|
|
71
|
+
*/
|
|
72
|
+
android_keyboardInputMode?: 'adjustPan' | 'adjustResize'
|
|
73
|
+
/** Sticky footer rendered below the scroll area. */
|
|
74
|
+
footer?: React.ReactNode
|
|
75
|
+
/**
|
|
76
|
+
* Array of snap points for the sheet (e.g., ['50%', '85%'] or [200, 500]).
|
|
77
|
+
* When provided, disables enableDynamicSizing.
|
|
78
|
+
* When omitted, sheet uses dynamic sizing (auto-fits content).
|
|
79
|
+
*/
|
|
80
|
+
snapPoints?: (string | number)[]
|
|
33
81
|
}
|
|
34
82
|
|
|
35
83
|
export function Sheet({
|
|
36
84
|
open,
|
|
37
85
|
onClose,
|
|
38
86
|
title,
|
|
87
|
+
subtitle,
|
|
39
88
|
description,
|
|
89
|
+
showCloseButton = false,
|
|
40
90
|
children,
|
|
41
91
|
style,
|
|
92
|
+
contentStyle,
|
|
42
93
|
scrollable,
|
|
43
94
|
maxHeight,
|
|
44
95
|
keyboardBehavior,
|
|
45
|
-
|
|
96
|
+
keyboardBlurBehavior = 'restore',
|
|
97
|
+
enableBlurKeyboardOnGesture = true,
|
|
98
|
+
android_keyboardInputMode = 'adjustPan',
|
|
99
|
+
footer,
|
|
100
|
+
snapPoints,
|
|
46
101
|
}: SheetProps) {
|
|
47
102
|
const { colors } = useTheme()
|
|
103
|
+
const insets = useSafeAreaInsets()
|
|
48
104
|
const ref = useRef<BottomSheetModal>(null)
|
|
105
|
+
|
|
106
|
+
// 'interactive' + 'adjustPan' works properly with enableDynamicSizing on both platforms
|
|
107
|
+
// 'fillParent' + 'adjustResize' causes restore issues (transparent gap when keyboard dismisses)
|
|
108
|
+
const effectiveKeyboardBehavior = keyboardBehavior ?? 'interactive'
|
|
49
109
|
|
|
50
110
|
useEffect(() => {
|
|
51
111
|
if (open) {
|
|
52
|
-
|
|
112
|
+
impactMedium()
|
|
53
113
|
ref.current?.present()
|
|
54
114
|
} else {
|
|
55
115
|
ref.current?.dismiss()
|
|
56
116
|
}
|
|
57
117
|
}, [open])
|
|
58
118
|
|
|
59
|
-
const renderBackdrop = (props: BottomSheetBackdropProps) => (
|
|
119
|
+
const renderBackdrop = useCallback((props: BottomSheetBackdropProps) => (
|
|
60
120
|
<BottomSheetBackdrop
|
|
61
121
|
{...props}
|
|
62
122
|
disappearsOnIndex={-1}
|
|
63
123
|
appearsOnIndex={0}
|
|
64
124
|
pressBehavior="close"
|
|
65
125
|
/>
|
|
66
|
-
)
|
|
126
|
+
), [])
|
|
127
|
+
|
|
128
|
+
const renderFooter = useCallback((props: BottomSheetFooterProps) => {
|
|
129
|
+
if (!footer) return null
|
|
130
|
+
return (
|
|
131
|
+
<BottomSheetFooter {...props}>
|
|
132
|
+
{footer}
|
|
133
|
+
</BottomSheetFooter>
|
|
134
|
+
)
|
|
135
|
+
}, [footer])
|
|
136
|
+
|
|
137
|
+
const effectiveSubtitle = subtitle ?? description
|
|
67
138
|
|
|
68
|
-
const
|
|
139
|
+
const showHeader = !!(title || effectiveSubtitle || showCloseButton)
|
|
140
|
+
|
|
141
|
+
const headerNode = showHeader ? (
|
|
69
142
|
<View style={styles.header}>
|
|
70
|
-
{
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
143
|
+
<View style={styles.headerRow}>
|
|
144
|
+
{title ? (
|
|
145
|
+
<Text style={[styles.title, { color: colors.foreground }]} allowFontScaling={true}>
|
|
146
|
+
{title}
|
|
147
|
+
</Text>
|
|
148
|
+
) : <View style={{ flex: 1 }} />}
|
|
149
|
+
{showCloseButton ? (
|
|
150
|
+
<TouchableOpacity
|
|
151
|
+
onPress={onClose}
|
|
152
|
+
style={styles.closeButton}
|
|
153
|
+
activeOpacity={0.6}
|
|
154
|
+
touchSoundDisabled={true}
|
|
155
|
+
>
|
|
156
|
+
<AntDesign name="close" size={ms(18)} color={colors.foregroundMuted} />
|
|
157
|
+
</TouchableOpacity>
|
|
158
|
+
) : null}
|
|
159
|
+
</View>
|
|
160
|
+
{effectiveSubtitle ? (
|
|
161
|
+
<Text style={[styles.subtitle, { color: colors.foregroundMuted }]} allowFontScaling={true}>
|
|
162
|
+
{effectiveSubtitle}
|
|
76
163
|
</Text>
|
|
77
164
|
) : null}
|
|
78
165
|
</View>
|
|
79
166
|
) : null
|
|
80
167
|
|
|
81
168
|
const useScroll = scrollable || !!maxHeight
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
<KeyboardAvoidingView behavior={behavior} keyboardVerticalOffset={keyboardOffset}>
|
|
88
|
-
{node}
|
|
89
|
-
</KeyboardAvoidingView>
|
|
90
|
-
)
|
|
91
|
-
}
|
|
92
|
-
|
|
169
|
+
const effectiveMaxHeight = maxHeight ?? DEFAULT_MAX_HEIGHT
|
|
170
|
+
|
|
171
|
+
// If snapPoints provided, disable dynamic sizing. Otherwise use dynamic sizing.
|
|
172
|
+
const useDynamicSizing = !snapPoints
|
|
173
|
+
|
|
93
174
|
return (
|
|
94
175
|
<BottomSheetModal
|
|
95
176
|
ref={ref}
|
|
96
|
-
enableDynamicSizing
|
|
177
|
+
enableDynamicSizing={useDynamicSizing}
|
|
178
|
+
snapPoints={snapPoints}
|
|
179
|
+
maxDynamicContentSize={useDynamicSizing ? effectiveMaxHeight : undefined}
|
|
97
180
|
onDismiss={onClose}
|
|
98
181
|
backdropComponent={renderBackdrop}
|
|
182
|
+
footerComponent={footer ? renderFooter : undefined}
|
|
99
183
|
backgroundStyle={[styles.background, { backgroundColor: colors.card }]}
|
|
100
184
|
handleIndicatorStyle={[styles.handle, { backgroundColor: colors.border }]}
|
|
101
185
|
enablePanDownToClose
|
|
186
|
+
topInset={insets.top}
|
|
187
|
+
keyboardBehavior={effectiveKeyboardBehavior}
|
|
188
|
+
keyboardBlurBehavior={keyboardBlurBehavior}
|
|
189
|
+
android_keyboardInputMode={android_keyboardInputMode}
|
|
190
|
+
enableBlurKeyboardOnGesture={enableBlurKeyboardOnGesture}
|
|
102
191
|
>
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
192
|
+
{useScroll ? (
|
|
193
|
+
<BottomSheetScrollView
|
|
194
|
+
contentContainerStyle={[
|
|
195
|
+
styles.scrollContent,
|
|
196
|
+
style,
|
|
197
|
+
]}
|
|
198
|
+
style={contentStyle}
|
|
199
|
+
showsVerticalScrollIndicator={true}
|
|
200
|
+
indicatorStyle="black"
|
|
201
|
+
persistentScrollbar={isAndroid}
|
|
202
|
+
>
|
|
203
|
+
{headerNode}
|
|
204
|
+
{children}
|
|
205
|
+
</BottomSheetScrollView>
|
|
206
|
+
) : (
|
|
207
|
+
<BottomSheetView style={[styles.content, contentStyle, style]}>
|
|
208
|
+
{headerNode}
|
|
209
|
+
{children}
|
|
210
|
+
</BottomSheetView>
|
|
211
|
+
)}
|
|
116
212
|
</BottomSheetModal>
|
|
117
213
|
)
|
|
118
214
|
}
|
|
@@ -127,21 +223,38 @@ const styles = StyleSheet.create({
|
|
|
127
223
|
height: vs(4),
|
|
128
224
|
borderRadius: ms(2),
|
|
129
225
|
},
|
|
130
|
-
content: {
|
|
131
|
-
paddingHorizontal: s(24),
|
|
132
|
-
paddingBottom: vs(32),
|
|
133
|
-
},
|
|
134
226
|
header: {
|
|
135
|
-
|
|
136
|
-
|
|
227
|
+
paddingHorizontal: s(16),
|
|
228
|
+
paddingTop: vs(4),
|
|
229
|
+
paddingBottom: vs(12),
|
|
230
|
+
gap: vs(4),
|
|
231
|
+
},
|
|
232
|
+
headerRow: {
|
|
233
|
+
flexDirection: 'row',
|
|
234
|
+
alignItems: 'center',
|
|
235
|
+
justifyContent: 'space-between',
|
|
137
236
|
},
|
|
138
237
|
title: {
|
|
139
238
|
fontFamily: 'Poppins-SemiBold',
|
|
140
239
|
fontSize: ms(18),
|
|
240
|
+
flex: 1,
|
|
141
241
|
},
|
|
142
|
-
|
|
242
|
+
subtitle: {
|
|
143
243
|
fontFamily: 'Poppins-Regular',
|
|
144
244
|
fontSize: ms(14),
|
|
145
245
|
lineHeight: mvs(20),
|
|
146
246
|
},
|
|
247
|
+
closeButton: {
|
|
248
|
+
padding: s(4),
|
|
249
|
+
marginLeft: s(8),
|
|
250
|
+
},
|
|
251
|
+
content: {
|
|
252
|
+
paddingHorizontal: s(16),
|
|
253
|
+
paddingBottom: vs(32),
|
|
254
|
+
},
|
|
255
|
+
scrollContent: {
|
|
256
|
+
paddingHorizontal: s(16),
|
|
257
|
+
paddingBottom: vs(32),
|
|
258
|
+
paddingRight: s(16),
|
|
259
|
+
},
|
|
147
260
|
})
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { Sheet, BottomSheetModalProvider } from './Sheet'
|
|
1
|
+
export { Sheet, BottomSheetModalProvider, SheetTextInput } from './Sheet'
|
|
2
2
|
export type { SheetProps } from './Sheet'
|
|
@@ -49,11 +49,11 @@ function TabTrigger({
|
|
|
49
49
|
const scale = useRef(new Animated.Value(1)).current
|
|
50
50
|
|
|
51
51
|
const handlePressIn = () => {
|
|
52
|
-
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver,
|
|
52
|
+
Animated.spring(scale, { toValue: 0.95, useNativeDriver: nativeDriver, stiffness: 600, damping: 35, mass: 0.8 }).start()
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
const handlePressOut = () => {
|
|
56
|
-
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver,
|
|
56
|
+
Animated.spring(scale, { toValue: 1, useNativeDriver: nativeDriver, stiffness: 280, damping: 22, mass: 0.8 }).start()
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const isUnderline = variant === 'underline'
|
|
@@ -108,8 +108,8 @@ export function Tabs({ tabs, variant = 'pill', value, onValueChange, children, s
|
|
|
108
108
|
if (!layout) return
|
|
109
109
|
if (animate) {
|
|
110
110
|
Animated.parallel([
|
|
111
|
-
Animated.spring(pillX, { toValue: layout.x, useNativeDriver: false,
|
|
112
|
-
Animated.spring(pillWidth, { toValue: layout.width, useNativeDriver: false,
|
|
111
|
+
Animated.spring(pillX, { toValue: layout.x, useNativeDriver: false, stiffness: 380, damping: 38, mass: 1.0 }),
|
|
112
|
+
Animated.spring(pillWidth, { toValue: layout.width, useNativeDriver: false, stiffness: 380, damping: 38, mass: 1.0 }),
|
|
113
113
|
]).start()
|
|
114
114
|
} else {
|
|
115
115
|
pillX.setValue(layout.x)
|