Files
MyClub/app/src/components/ui/ModernComponents.tsx
T
Tomas Dvorak dfc079288f hot fix #1
2026-01-26 08:13:18 +01:00

309 lines
7.8 KiB
TypeScript

import React from 'react';
import { View, Text, StyleSheet, TextInput, TouchableOpacity } from 'react-native';
import { useClubTheme } from '../../theme';
interface ModernCardProps {
children: React.ReactNode;
style?: any;
shadow?: boolean;
padding?: number;
margin?: number;
borderRadius?: number;
}
export const ModernCard: React.FC<ModernCardProps> = ({
children,
style,
shadow = true,
padding = 16,
margin = 0,
borderRadius = 16,
}) => {
const { theme } = useClubTheme();
const cardStyle = {
padding,
margin,
borderRadius,
backgroundColor: '#FFFFFF',
borderLeftWidth: 4,
borderLeftColor: theme.primary,
...(shadow && {
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4,
}),
...style,
};
return <View style={cardStyle}>{children}</View>;
};
interface ModernButtonProps {
title: string;
onPress: () => void;
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
size?: 'small' | 'medium' | 'large';
disabled?: boolean;
loading?: boolean;
style?: any;
textStyle?: any;
icon?: string;
}
export const ModernButton: React.FC<ModernButtonProps> = ({
title,
onPress,
variant = 'primary',
size = 'medium',
disabled = false,
loading = false,
style,
textStyle,
icon,
}) => {
const { theme } = useClubTheme();
const getButtonStyle = () => {
let baseStyle: any = {
alignItems: 'center' as const,
justifyContent: 'center' as const,
borderRadius: 12,
};
// Size variants
if (size === 'small') {
baseStyle = { ...baseStyle, paddingHorizontal: 12, paddingVertical: 8 };
} else if (size === 'large') {
baseStyle = { ...baseStyle, paddingHorizontal: 24, paddingVertical: 16 };
} else {
baseStyle = { ...baseStyle, paddingHorizontal: 20, paddingVertical: 12 };
}
// Variant styles
if (variant === 'primary') {
baseStyle = { ...baseStyle, backgroundColor: theme.primary };
} else if (variant === 'secondary') {
baseStyle = { ...baseStyle, backgroundColor: theme.secondary };
} else if (variant === 'outline') {
baseStyle = { ...baseStyle, backgroundColor: 'transparent', borderWidth: 2, borderColor: theme.primary };
} else {
baseStyle = { ...baseStyle, backgroundColor: 'transparent' };
}
if (disabled) {
baseStyle = { ...baseStyle, opacity: 0.5 };
}
return { ...baseStyle, ...style };
};
const getTextStyle = () => {
let baseStyle: any = {
fontWeight: '600' as const,
textAlign: 'center' as const,
};
if (size === 'small') {
baseStyle = { ...baseStyle, fontSize: 14 };
} else if (size === 'large') {
baseStyle = { ...baseStyle, fontSize: 18 };
} else {
baseStyle = { ...baseStyle, fontSize: 16 };
}
if (variant === 'outline' || variant === 'ghost') {
baseStyle = { ...baseStyle, color: theme.primary };
} else {
baseStyle = { ...baseStyle, color: '#FFFFFF' };
}
if (disabled) {
baseStyle = { ...baseStyle, opacity: 0.7 };
}
return { ...baseStyle, ...textStyle };
};
return (
<TouchableOpacity
style={getButtonStyle()}
onPress={onPress}
disabled={disabled || loading}
>
<View style={{ flexDirection: 'row', alignItems: 'center', gap: icon ? 8 : 0 }}>
{icon && <Text style={getTextStyle()}>{icon}</Text>}
<Text style={getTextStyle()}>
{loading ? 'Načítám...' : title}
</Text>
</View>
</TouchableOpacity>
);
};
interface ModernInputProps {
value: string;
onChangeText: (text: string) => void;
placeholder?: string;
label?: string;
error?: string;
icon?: string;
secureTextEntry?: boolean;
keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad';
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
style?: any;
}
export const ModernInput: React.FC<ModernInputProps> = ({
value,
onChangeText,
placeholder,
label,
error,
icon,
secureTextEntry = false,
keyboardType = 'default',
autoCapitalize = 'sentences',
style,
}) => {
const { theme } = useClubTheme();
return (
<View style={[{ marginBottom: 16 }, style]}>
{label && <Text style={{ fontSize: 14, fontWeight: '600', marginBottom: 8, color: '#374151' }}>{label}</Text>}
<View style={[
{ flexDirection: 'row', alignItems: 'center', borderWidth: 1, borderRadius: 12, paddingHorizontal: 16, paddingVertical: 12, backgroundColor: '#FFFFFF' },
error && { borderWidth: 2, borderColor: '#EF4444' },
!error && { borderColor: theme.primary + '30' }
]}>
{icon && <Text style={{ marginRight: 12, fontSize: 16 }}>{icon}</Text>}
<TextInput
style={[
{ flex: 1, fontSize: 16 },
{ color: theme.text }
]}
value={value}
onChangeText={onChangeText}
placeholder={placeholder}
placeholderTextColor="#9CA3AF"
secureTextEntry={secureTextEntry}
keyboardType={keyboardType}
autoCapitalize={autoCapitalize}
/>
</View>
{error && <Text style={{ fontSize: 12, color: '#EF4444', marginTop: 4 }}>{error}</Text>}
</View>
);
};
interface ModernBadgeProps {
text: string;
variant?: 'primary' | 'secondary' | 'success' | 'warning' | 'error' | 'outline';
size?: 'small' | 'medium' | 'large';
style?: any;
}
export const ModernBadge: React.FC<ModernBadgeProps> = ({
text,
variant = 'primary',
size = 'medium',
style,
}) => {
const { theme } = useClubTheme();
const getBadgeStyle = () => {
let baseStyle: any = {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 999,
alignSelf: 'flex-start' as const,
};
// Size variants
if (size === 'small') {
baseStyle = { ...baseStyle, paddingHorizontal: 6, paddingVertical: 2 };
} else if (size === 'large') {
baseStyle = { ...baseStyle, paddingHorizontal: 12, paddingVertical: 6 };
}
// Variant colors
if (variant === 'primary') {
baseStyle = { ...baseStyle, backgroundColor: theme.primary };
} else if (variant === 'secondary') {
baseStyle = { ...baseStyle, backgroundColor: theme.secondary };
} else if (variant === 'success') {
baseStyle = { ...baseStyle, backgroundColor: '#10B981' };
} else if (variant === 'warning') {
baseStyle = { ...baseStyle, backgroundColor: '#F59E0B' };
} else if (variant === 'error') {
baseStyle = { ...baseStyle, backgroundColor: '#EF4444' };
} else if (variant === 'outline') {
baseStyle = { ...baseStyle, backgroundColor: 'transparent', borderWidth: 1, borderColor: theme.primary };
}
return { ...baseStyle, ...style };
};
return (
<View style={getBadgeStyle()}>
<Text style={{ fontSize: 12, fontWeight: '600', color: variant === 'outline' ? theme.primary : '#FFFFFF' }}>{text}</Text>
</View>
);
};
const styles = StyleSheet.create({
inputContainer: {
flexDirection: 'row',
alignItems: 'center',
borderWidth: 1,
borderRadius: 12,
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: '#FFFFFF',
},
inputError: {
borderWidth: 2,
},
inputIcon: {
marginRight: 12,
fontSize: 16,
},
input: {
flex: 1,
fontSize: 16,
},
errorText: {
fontSize: 12,
color: '#EF4444',
marginTop: 4,
},
badge: {
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 999,
alignSelf: 'flex-start',
},
badgeSmall: {
paddingHorizontal: 6,
paddingVertical: 2,
},
badgeLarge: {
paddingHorizontal: 12,
paddingVertical: 6,
},
badgeText: {
fontSize: 12,
fontWeight: '600',
color: '#FFFFFF',
},
});
export default {
ModernCard,
ModernButton,
ModernInput,
ModernBadge,
};