14c880123c
Expo app with dashboard, time clock, invoices, and settings — native tabs, glass UI, theme-aware components, and iOS Live Activities. Co-authored-by: Cursor <cursoragent@cursor.com>
96 lines
2.1 KiB
TypeScript
96 lines
2.1 KiB
TypeScript
import {
|
|
ActivityIndicator,
|
|
Pressable,
|
|
StyleSheet,
|
|
Text,
|
|
type PressableProps,
|
|
type ViewStyle,
|
|
} from "react-native";
|
|
|
|
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;
|
|
};
|
|
|
|
export function Button({
|
|
title,
|
|
loading,
|
|
variant = "primary",
|
|
disabled,
|
|
style,
|
|
...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}
|
|
/>
|
|
) : (
|
|
<Text style={[styles.label, labelStyles[variant]]}>{title}</Text>
|
|
)}
|
|
</Pressable>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
base: {
|
|
minHeight: 40,
|
|
borderRadius: radii.md,
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
paddingHorizontal: spacing.md,
|
|
},
|
|
pressed: {
|
|
opacity: 0.92,
|
|
},
|
|
disabled: {
|
|
opacity: 0.55,
|
|
},
|
|
label: {
|
|
fontSize: 14,
|
|
fontFamily: fonts.bodyMedium,
|
|
},
|
|
});
|