1f6acc11ab
* package upgrades
* upgrade system ui
* update patches
* rename patch
* rm
* use .set/.set
* resolve yarnlock
* fix accidentally removed package
* fix use permissions hook
* fix some type errors
* type fixes
* more tweaking
* clean
* Discard changes to src/screens/Onboarding/StepProfile/index.tsx
* oops
* fix splash
* use ios/android in config
* Fix tests
* add back patch
* add to rn patch
* fullscreen?
* Revert "add to rn patch"
This reverts commit 4716d2c643.
* try this
* test with revert
* test
* maybe this
* fix config
* Bump @react-native-picker/picker
* Bump some packages
* Rm unused
* Update lockfile
* Rename expo-notifications+0.29.8.patch.md to expo-notifications+0.29.10.patch.md
* Update react-native+0.76.3.patch.md
* Update react-native+0.76.3.patch.md
* Inline splash configs
Jumping around the file is annoying and makes it harder to understand how this is structured.
* Start fixing Android splash
* Downgrade compressor
This version isn't building for me due to https://github.com/numandev1/react-native-compressor/issues/322.
* Make Android splash empty for now
* Work around a bug
* Bump the compressor
* Bump again
* Include splash fixes
* Try updating
* No custom Android splash
* Revert to using icons
* welp
* Fix sizes
* Make sizing work
* Bump size
---------
Co-authored-by: Dan Abramov <dan.abramov@gmail.com>
226 lines
5.8 KiB
TypeScript
226 lines
5.8 KiB
TypeScript
import React from 'react'
|
|
import {
|
|
ActivityIndicator,
|
|
GestureResponderEvent,
|
|
NativeSyntheticEvent,
|
|
NativeTouchEvent,
|
|
Pressable,
|
|
PressableStateCallbackType,
|
|
StyleProp,
|
|
StyleSheet,
|
|
TextStyle,
|
|
View,
|
|
ViewStyle,
|
|
} from 'react-native'
|
|
|
|
import {choose} from '#/lib/functions'
|
|
import {useTheme} from '#/lib/ThemeContext'
|
|
import {Text} from '../text/Text'
|
|
|
|
export type ButtonType =
|
|
| 'primary'
|
|
| 'secondary'
|
|
| 'default'
|
|
| 'inverted'
|
|
| 'primary-outline'
|
|
| 'secondary-outline'
|
|
| 'primary-light'
|
|
| 'secondary-light'
|
|
| 'default-light'
|
|
|
|
// Augment type for react-native-web (see https://github.com/necolas/react-native-web/issues/1684#issuecomment-766451866)
|
|
declare module 'react-native' {
|
|
interface PressableStateCallbackType {
|
|
// @ts-ignore web only
|
|
hovered?: boolean
|
|
focused?: boolean
|
|
}
|
|
}
|
|
|
|
// TODO: Enforce that button always has a label
|
|
export function Button({
|
|
type = 'primary',
|
|
label,
|
|
style,
|
|
labelContainerStyle,
|
|
labelStyle,
|
|
onPress,
|
|
children,
|
|
testID,
|
|
accessibilityLabel,
|
|
accessibilityHint,
|
|
accessibilityLabelledBy,
|
|
onAccessibilityEscape,
|
|
withLoading = false,
|
|
disabled = false,
|
|
}: React.PropsWithChildren<{
|
|
type?: ButtonType
|
|
label?: string
|
|
style?: StyleProp<ViewStyle>
|
|
labelContainerStyle?: StyleProp<ViewStyle>
|
|
labelStyle?: StyleProp<TextStyle>
|
|
onPress?: (e: NativeSyntheticEvent<NativeTouchEvent>) => void | Promise<void>
|
|
testID?: string
|
|
accessibilityLabel?: string
|
|
accessibilityHint?: string
|
|
accessibilityLabelledBy?: string
|
|
onAccessibilityEscape?: () => void
|
|
withLoading?: boolean
|
|
disabled?: boolean
|
|
}>) {
|
|
const theme = useTheme()
|
|
const typeOuterStyle = choose<ViewStyle, Record<ButtonType, ViewStyle>>(
|
|
type,
|
|
{
|
|
primary: {
|
|
backgroundColor: theme.palette.primary.background,
|
|
},
|
|
secondary: {
|
|
backgroundColor: theme.palette.secondary.background,
|
|
},
|
|
default: {
|
|
backgroundColor: theme.palette.default.backgroundLight,
|
|
},
|
|
inverted: {
|
|
backgroundColor: theme.palette.inverted.background,
|
|
},
|
|
'primary-outline': {
|
|
backgroundColor: theme.palette.default.background,
|
|
borderWidth: 1,
|
|
borderColor: theme.palette.primary.border,
|
|
},
|
|
'secondary-outline': {
|
|
backgroundColor: theme.palette.default.background,
|
|
borderWidth: 1,
|
|
borderColor: theme.palette.secondary.border,
|
|
},
|
|
'primary-light': {
|
|
backgroundColor: theme.palette.default.background,
|
|
},
|
|
'secondary-light': {
|
|
backgroundColor: theme.palette.default.background,
|
|
},
|
|
'default-light': {
|
|
backgroundColor: theme.palette.default.background,
|
|
},
|
|
},
|
|
)
|
|
const typeLabelStyle = choose<TextStyle, Record<ButtonType, TextStyle>>(
|
|
type,
|
|
{
|
|
primary: {
|
|
color: theme.palette.primary.text,
|
|
fontWeight: '600',
|
|
},
|
|
secondary: {
|
|
color: theme.palette.secondary.text,
|
|
fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined,
|
|
},
|
|
default: {
|
|
color: theme.palette.default.text,
|
|
},
|
|
inverted: {
|
|
color: theme.palette.inverted.text,
|
|
fontWeight: '600',
|
|
},
|
|
'primary-outline': {
|
|
color: theme.palette.primary.textInverted,
|
|
fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined,
|
|
},
|
|
'secondary-outline': {
|
|
color: theme.palette.secondary.textInverted,
|
|
fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined,
|
|
},
|
|
'primary-light': {
|
|
color: theme.palette.primary.textInverted,
|
|
fontWeight: theme.palette.primary.isLowContrast ? '600' : undefined,
|
|
},
|
|
'secondary-light': {
|
|
color: theme.palette.secondary.textInverted,
|
|
fontWeight: theme.palette.secondary.isLowContrast ? '600' : undefined,
|
|
},
|
|
'default-light': {
|
|
color: theme.palette.default.text,
|
|
fontWeight: theme.palette.default.isLowContrast ? '600' : undefined,
|
|
},
|
|
},
|
|
)
|
|
|
|
const [isLoading, setIsLoading] = React.useState(false)
|
|
const onPressWrapped = React.useCallback(
|
|
async (event: GestureResponderEvent) => {
|
|
event.stopPropagation()
|
|
event.preventDefault()
|
|
withLoading && setIsLoading(true)
|
|
await onPress?.(event)
|
|
withLoading && setIsLoading(false)
|
|
},
|
|
[onPress, withLoading],
|
|
)
|
|
|
|
const getStyle = React.useCallback(
|
|
(state: PressableStateCallbackType) => {
|
|
const arr = [typeOuterStyle, styles.outer, style]
|
|
if (state.pressed) {
|
|
arr.push({opacity: 0.6})
|
|
} else if (state.hovered) {
|
|
arr.push({opacity: 0.8})
|
|
}
|
|
return arr
|
|
},
|
|
[typeOuterStyle, style],
|
|
)
|
|
|
|
const renderChildern = React.useCallback(() => {
|
|
if (!label) {
|
|
return children
|
|
}
|
|
|
|
return (
|
|
<View style={[styles.labelContainer, labelContainerStyle]}>
|
|
{label && withLoading && isLoading ? (
|
|
<ActivityIndicator size={12} color={typeLabelStyle.color} />
|
|
) : null}
|
|
<Text type="button" style={[typeLabelStyle, labelStyle]}>
|
|
{label}
|
|
</Text>
|
|
</View>
|
|
)
|
|
}, [
|
|
children,
|
|
label,
|
|
withLoading,
|
|
isLoading,
|
|
labelContainerStyle,
|
|
typeLabelStyle,
|
|
labelStyle,
|
|
])
|
|
|
|
return (
|
|
<Pressable
|
|
style={getStyle}
|
|
onPress={onPressWrapped}
|
|
disabled={disabled || isLoading}
|
|
testID={testID}
|
|
accessibilityRole="button"
|
|
accessibilityLabel={accessibilityLabel}
|
|
accessibilityHint={accessibilityHint}
|
|
accessibilityLabelledBy={accessibilityLabelledBy}
|
|
onAccessibilityEscape={onAccessibilityEscape}>
|
|
{renderChildern}
|
|
</Pressable>
|
|
)
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
outer: {
|
|
paddingHorizontal: 14,
|
|
paddingVertical: 8,
|
|
borderRadius: 24,
|
|
},
|
|
labelContainer: {
|
|
flexDirection: 'row',
|
|
gap: 8,
|
|
},
|
|
})
|