mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at samuel/patch-onpaste 227 lines 6.0 kB view raw
1import React from 'react' 2import { 3 ActivityIndicator, 4 type GestureResponderEvent, 5 type NativeSyntheticEvent, 6 type NativeTouchEvent, 7 Pressable, 8 type PressableStateCallbackType, 9 type StyleProp, 10 StyleSheet, 11 type TextStyle, 12 View, 13 type ViewStyle, 14} from 'react-native' 15 16import {choose} from '#/lib/functions' 17import {useTheme} from '#/lib/ThemeContext' 18import {Text} from '../text/Text' 19 20export type ButtonType = 21 | 'primary' 22 | 'secondary' 23 | 'default' 24 | 'inverted' 25 | 'primary-outline' 26 | 'secondary-outline' 27 | 'primary-light' 28 | 'secondary-light' 29 | 'default-light' 30 31// Augment type for react-native-web (see https://github.com/necolas/react-native-web/issues/1684#issuecomment-766451866) 32declare module 'react-native' { 33 interface PressableStateCallbackType { 34 // @ts-ignore web only 35 hovered?: boolean 36 focused?: boolean 37 } 38} 39 40/** 41 * @deprecated use Button from `#/components/Button.tsx` instead 42 */ 43export function Button({ 44 type = 'primary', 45 label, 46 style, 47 labelContainerStyle, 48 labelStyle, 49 onPress, 50 children, 51 testID, 52 accessibilityLabel, 53 accessibilityHint, 54 accessibilityLabelledBy, 55 onAccessibilityEscape, 56 withLoading = false, 57 disabled = false, 58}: React.PropsWithChildren<{ 59 type?: ButtonType 60 label?: string 61 style?: StyleProp<ViewStyle> 62 labelContainerStyle?: StyleProp<ViewStyle> 63 labelStyle?: StyleProp<TextStyle> 64 onPress?: (e: NativeSyntheticEvent<NativeTouchEvent>) => void | Promise<void> 65 testID?: string 66 accessibilityLabel?: string 67 accessibilityHint?: string 68 accessibilityLabelledBy?: string 69 onAccessibilityEscape?: () => void 70 withLoading?: boolean 71 disabled?: boolean 72}>) { 73 const theme = useTheme() 74 const typeOuterStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>( 75 type, 76 { 77 primary: { 78 backgroundColor: theme.palette.primary.background, 79 }, 80 secondary: { 81 backgroundColor: theme.palette.secondary.background, 82 }, 83 default: { 84 backgroundColor: theme.palette.default.backgroundLight, 85 }, 86 inverted: { 87 backgroundColor: theme.palette.inverted.background, 88 }, 89 'primary-outline': { 90 backgroundColor: theme.palette.default.background, 91 borderWidth: 1, 92 borderColor: theme.palette.primary.border, 93 }, 94 'secondary-outline': { 95 backgroundColor: theme.palette.default.background, 96 borderWidth: 1, 97 borderColor: theme.palette.secondary.border, 98 }, 99 'primary-light': { 100 backgroundColor: theme.palette.default.background, 101 }, 102 'secondary-light': { 103 backgroundColor: theme.palette.default.background, 104 }, 105 'default-light': { 106 backgroundColor: theme.palette.default.background, 107 }, 108 }, 109 ) 110 const typeLabelStyle = choose<TextStyle, Record<ButtonType, TextStyle>>( 111 type, 112 { 113 primary: { 114 color: theme.palette.primary.text, 115 fontWeight: '600', 116 }, 117 secondary: { 118 color: theme.palette.secondary.text, 119 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 120 }, 121 default: { 122 color: theme.palette.default.text, 123 }, 124 inverted: { 125 color: theme.palette.inverted.text, 126 fontWeight: '600', 127 }, 128 'primary-outline': { 129 color: theme.palette.primary.textInverted, 130 fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined, 131 }, 132 'secondary-outline': { 133 color: theme.palette.secondary.textInverted, 134 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 135 }, 136 'primary-light': { 137 color: theme.palette.primary.textInverted, 138 fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined, 139 }, 140 'secondary-light': { 141 color: theme.palette.secondary.textInverted, 142 fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined, 143 }, 144 'default-light': { 145 color: theme.palette.default.text, 146 fontWeight: theme.palette.default.isLowContrast ? '600' : undefined, 147 }, 148 }, 149 ) 150 151 const [isLoading, setIsLoading] = React.useState(false) 152 const onPressWrapped = React.useCallback( 153 async (event: GestureResponderEvent) => { 154 event.stopPropagation() 155 event.preventDefault() 156 withLoading && setIsLoading(true) 157 await onPress?.(event) 158 withLoading && setIsLoading(false) 159 }, 160 [onPress, withLoading], 161 ) 162 163 const getStyle = React.useCallback( 164 (state: PressableStateCallbackType) => { 165 const arr = [typeOuterStyle, styles.outer, style] 166 if (state.pressed) { 167 arr.push({opacity: 0.6}) 168 } else if (state.hovered) { 169 arr.push({opacity: 0.8}) 170 } 171 return arr 172 }, 173 [typeOuterStyle, style], 174 ) 175 176 const renderChildern = React.useCallback(() => { 177 if (!label) { 178 return children 179 } 180 181 return ( 182 <View style={[styles.labelContainer, labelContainerStyle]}> 183 {label && withLoading && isLoading ? ( 184 <ActivityIndicator size={12} color={typeLabelStyle.color} /> 185 ) : null} 186 <Text type="button" style={[typeLabelStyle, labelStyle]}> 187 {label} 188 </Text> 189 </View> 190 ) 191 }, [ 192 children, 193 label, 194 withLoading, 195 isLoading, 196 labelContainerStyle, 197 typeLabelStyle, 198 labelStyle, 199 ]) 200 201 return ( 202 <Pressable 203 style={getStyle} 204 onPress={onPressWrapped} 205 disabled={disabled || isLoading} 206 testID={testID} 207 accessibilityRole="button" 208 accessibilityLabel={accessibilityLabel} 209 accessibilityHint={accessibilityHint} 210 accessibilityLabelledBy={accessibilityLabelledBy} 211 onAccessibilityEscape={onAccessibilityEscape}> 212 {renderChildern} 213 </Pressable> 214 ) 215} 216 217const styles = StyleSheet.create({ 218 outer: { 219 paddingHorizontal: 14, 220 paddingVertical: 8, 221 borderRadius: 24, 222 }, 223 labelContainer: { 224 flexDirection: 'row', 225 gap: 8, 226 }, 227})