feat : Badge et Chip
This commit is contained in:
131
src/components/ThemeChip.tsx
Normal file
131
src/components/ThemeChip.tsx
Normal file
@@ -0,0 +1,131 @@
|
||||
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',
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user