|
1 | 1 | import MaterialIcons from '@expo/vector-icons/MaterialIcons'; |
2 | | -import type { ComponentProps } from 'react'; |
3 | | -import { ActivityIndicator, Animated, Pressable, Text, View } from 'react-native'; |
| 2 | +import type { ComponentProps, Ref } from 'react'; |
| 3 | +import { ActivityIndicator, Animated, Pressable, Text, View, type View as NativeView } from 'react-native'; |
4 | 4 | import { useTheme } from '@/theme/ThemeContext'; |
5 | 5 | import { usePressScale } from './usePressScale'; |
6 | 6 |
|
7 | 7 | type Tone = 'primary' | 'secondary' | 'tertiary' | 'danger'; |
8 | 8 | type IconName = ComponentProps<typeof MaterialIcons>['name']; |
9 | 9 |
|
10 | | -export function ActionButton({ label, onPress, disabled = false, busy = false, secondary = false, tone, icon }: { label: string; onPress: () => void; disabled?: boolean; busy?: boolean; secondary?: boolean; tone?: Tone; icon?: IconName }) { |
| 10 | +export function ActionButton({ label, onPress, disabled = false, busy = false, secondary = false, tone, icon, controlRef }: { label: string; onPress: () => void; disabled?: boolean; busy?: boolean; secondary?: boolean; tone?: Tone; icon?: IconName; controlRef?: Ref<NativeView> }) { |
11 | 11 | const { colors, radii, spacing, typography } = useTheme(); |
12 | 12 | const press = usePressScale(); |
13 | 13 | const resolvedTone = tone ?? (secondary ? 'secondary' : 'primary'); |
14 | 14 | const background = resolvedTone === 'primary' ? colors.brand : resolvedTone === 'danger' ? colors.dangerTint : resolvedTone === 'secondary' ? colors.surfaceRaised : 'transparent'; |
15 | 15 | const foreground = resolvedTone === 'primary' ? colors.onBrand : resolvedTone === 'danger' ? colors.danger : resolvedTone === 'tertiary' ? colors.brandText : colors.text; |
16 | 16 | return ( |
17 | | - <Animated.View style={{ transform: [{ scale: press.scale }], width: '100%' }}><Pressable accessibilityRole="button" accessibilityLabel={label} accessibilityState={{ disabled, busy }} disabled={disabled} onPress={onPress} onPressIn={press.pressIn} onPressOut={press.pressOut} style={({ pressed }) => ({ alignItems: 'center', backgroundColor: pressed && !disabled ? (resolvedTone === 'primary' ? colors.brandPressed : colors.surfacePressed) : background, borderColor: resolvedTone === 'tertiary' ? 'transparent' : resolvedTone === 'danger' ? colors.danger : colors.border, borderRadius: radii.md, borderWidth: resolvedTone === 'primary' ? 0 : 1, justifyContent: 'center', minHeight: 52, opacity: disabled ? 0.4 : 1, paddingHorizontal: spacing.lg })}> |
| 17 | + <Animated.View style={{ transform: [{ scale: press.scale }], width: '100%' }}><Pressable ref={controlRef} accessibilityRole="button" accessibilityLabel={label} accessibilityState={{ disabled, busy }} disabled={disabled} onPress={onPress} onPressIn={press.pressIn} onPressOut={press.pressOut} style={({ pressed }) => ({ alignItems: 'center', backgroundColor: pressed && !disabled ? (resolvedTone === 'primary' ? colors.brandPressed : colors.surfacePressed) : background, borderColor: resolvedTone === 'tertiary' ? 'transparent' : resolvedTone === 'danger' ? colors.danger : colors.border, borderRadius: radii.md, borderWidth: resolvedTone === 'primary' ? 0 : 1, justifyContent: 'center', minHeight: 52, opacity: disabled ? 0.4 : 1, paddingHorizontal: spacing.lg })}> |
18 | 18 | <View style={{ alignItems: 'center', flexDirection: 'row', gap: spacing.sm }}> |
19 | 19 | {busy ? <ActivityIndicator color={foreground} /> : icon ? <MaterialIcons color={foreground} importantForAccessibility="no" name={icon} size={20} /> : null} |
20 | 20 | <Text style={[typography.label, { color: foreground, flexShrink: 1, textAlign: 'center' }]}>{label}</Text> |
|
0 commit comments