Files
beenvoice-app/components/ui/Button.tsx
T
soconnorandCursor 6762a9bff3 Move production to beenvoice.app with migrated accounts, refreshed auth and timer UX, and expanded invoice flows.
Official URL migration preserves sessions, shortcuts prefs, and last clock-in client; auth screens match web with legal links; time clock and invoice editor/send flows are updated for the new domain and UI patterns.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-26 03:40:48 -04:00

118 lines
2.5 KiB
TypeScript

import {
ActivityIndicator,
Pressable,
StyleSheet,
Text,
View,
type PressableProps,
type ViewStyle,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { useAppTheme } from "@/contexts/ThemeContext";
import { fonts, radii, spacing } from "@/constants/theme";
type ButtonProps = PressableProps & {
title: string;
loading?: boolean;
variant?: "primary" | "secondary" | "danger" | "ghost";
style?: ViewStyle;
showArrow?: boolean;
};
export function Button({
title,
loading,
variant = "primary",
disabled,
style,
showArrow = false,
...props
}: ButtonProps) {
const { colors } = useAppTheme();
const isDisabled = disabled || loading;
const variantStyles = {
primary: { backgroundColor: colors.primary },
secondary: {
backgroundColor: colors.muted,
borderWidth: 1,
borderColor: colors.border,
},
danger: {
backgroundColor: colors.destructiveBg,
borderWidth: 1,
borderColor: colors.destructive,
},
ghost: { backgroundColor: "transparent" },
} as const;
const labelStyles = {
primary: { color: colors.primaryForeground },
secondary: { color: colors.foreground },
danger: { color: colors.destructive },
ghost: { color: colors.foreground },
} as const;
return (
<Pressable
accessibilityRole="button"
disabled={isDisabled}
style={({ pressed }) => [
styles.base,
variantStyles[variant],
pressed && !isDisabled && styles.pressed,
isDisabled && styles.disabled,
style,
]}
{...props}
>
{loading ? (
<ActivityIndicator
color={variant === "primary" ? colors.primaryForeground : colors.primary}
/>
) : (
<View style={styles.content}>
<Text style={[styles.label, labelStyles[variant]]}>{title}</Text>
{showArrow ? (
<Ionicons
name="arrow-forward"
size={16}
color={labelStyles[variant].color}
style={styles.arrow}
/>
) : null}
</View>
)}
</Pressable>
);
}
const styles = StyleSheet.create({
base: {
minHeight: 44,
borderRadius: radii.lg,
alignItems: "center",
justifyContent: "center",
paddingHorizontal: spacing.md,
},
content: {
flexDirection: "row",
alignItems: "center",
gap: spacing.sm,
},
arrow: {
marginTop: 1,
},
pressed: {
opacity: 0.92,
},
disabled: {
opacity: 0.55,
},
label: {
fontSize: 14,
fontFamily: fonts.bodyMedium,
},
});