Files
Bahla-Frontend/src/components/ThemeChip.tsx
2026-09-01 14:43:51 +02:00

132 lines
3.3 KiB
TypeScript

import { LinearGradient } from 'expo-linear-gradient';
import { X } from 'lucide-react-native';
import { useCallback } from 'react';
import {
Pressable,
type StyleProp,
StyleSheet,
Text,
type TextStyle,
View,
type ViewStyle,
} from 'react-native';
import { useTheme } from '@/theme/useTheme';
export type ThemeChipSelectedVariant = 'gradient' | 'accent';
export type ThemeChipProps = {
label: string;
value?: string;
selected?: boolean;
selectedVariant?: ThemeChipSelectedVariant;
disabled?: boolean;
showCross?: boolean;
showRemoveIcon?: boolean;
onSelect?: (value: string, nextSelected: boolean) => void;
style?: StyleProp<ViewStyle>;
textStyle?: StyleProp<TextStyle>;
testID?: string;
accessibilityLabel?: string;
};
const CHIP_HEIGHT = 34;
export function ThemeChip({
label,
value,
selected = false,
selectedVariant = 'accent',
disabled = false,
showCross,
showRemoveIcon = true,
onSelect,
style,
textStyle,
testID,
accessibilityLabel,
}: ThemeChipProps) {
const { colors, gradients, gradientDirection, borderRadius, typography } = useTheme();
const resolvedValue = value ?? label;
const isGradient = selected && selectedVariant === 'gradient';
const showSelectedIcon = selected && (showCross ?? showRemoveIcon);
const handlePress = useCallback(() => {
if (disabled) {
return;
}
onSelect?.(resolvedValue, !selected);
}, [disabled, onSelect, resolvedValue, selected]);
const containerStyle: ViewStyle = {
minHeight: CHIP_HEIGHT,
borderRadius: borderRadius.chip,
paddingHorizontal: 14,
borderWidth: 1,
alignSelf: 'flex-start',
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
gap: 8,
opacity: disabled ? 0.45 : 1,
};
const accentSelectedStyle: ViewStyle = {
backgroundColor: colors.primarySoft,
borderColor: colors.primary,
};
const idleStyle: ViewStyle = {
backgroundColor: colors.surface,
borderColor: '#2A2A3C',
};
const textColor = isGradient
? colors.textPrimary
: selected
? colors.textOnPrimarySoft
: colors.textPrimary;
return (
<Pressable
testID={testID}
disabled={disabled}
onPress={handlePress}
accessibilityRole="button"
accessibilityState={{ disabled, selected }}
accessibilityLabel={accessibilityLabel ?? label}
style={[styles.root, style]}
>
{isGradient ? (
<LinearGradient
colors={gradients.signature}
start={gradientDirection.start}
end={gradientDirection.end}
style={[containerStyle, { borderColor: 'transparent' }]}
>
<Text style={[styles.label, typography.chip, { color: textColor }, textStyle]}>{label}</Text>
{showSelectedIcon ? <X color={textColor} size={12} strokeWidth={2.3} /> : null}
</LinearGradient>
) : (
<View style={[containerStyle, selected ? accentSelectedStyle : idleStyle]}>
<Text style={[styles.label, typography.chip, { color: textColor }, textStyle]}>{label}</Text>
{showSelectedIcon ? <X color={textColor} size={12} strokeWidth={2.3} /> : null}
</View>
)}
</Pressable>
);
}
export default ThemeChip;
const styles = StyleSheet.create({
root: {
alignSelf: 'flex-start',
},
label: {
fontWeight: '600',
},
});