chahinebrini 14452b2a46 refactor(native): Pressable → TouchableOpacity sweep (style-fn swallows Android styles)
Alle <Pressable style={({pressed}) => ({...})}> ersetzt — style-Funktion
droppt auf Android (New Arch) intermittierend width/height, führt zu 0×0
unsichtbaren Elementen. TouchableOpacity mit activeOpacity ist stabil.

Außerdem übrige Pressables (plain style) aus components/ und app/
migriert sowie zwei überschüssige </View>-Tags in chat.tsx + RoomCard.tsx
entfernt die TS-Fehler verursacht haben.

64 Dateien, typecheck sauber.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:43:10 +02:00

106 lines
2.4 KiB
TypeScript

// RN-Port von apps/rebreak/app/components/StarRating.vue
// Unterstützt fractional values (z.B. 3.7) via width-clipping.
import { View, TouchableOpacity } from 'react-native';
import { Ionicons } from '@expo/vector-icons';
import { useState } from 'react';
export type StarSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
const sizeMap: Record<StarSize, number> = {
xs: 14,
sm: 18,
md: 20,
lg: 28,
xl: 40,
};
export interface StarRatingProps {
value?: number;
max?: number;
size?: StarSize;
interactive?: boolean;
filledColor?: string;
emptyColor?: string;
onChange?: (value: number) => void;
}
export function StarRating({
value = 0,
max = 5,
size = 'md',
interactive = false,
filledColor = '#facc15',
emptyColor = '#e5e7eb',
onChange,
}: StarRatingProps) {
const [hover, setHover] = useState(0);
const px = sizeMap[size];
const display = interactive ? hover || value : value;
const stars = [];
for (let i = 1; i <= max; i++) {
const filledRatio = Math.min(Math.max(display - (i - 1), 0), 1);
const filledWidth = filledRatio * px;
const star = (
<View
key={`star-${i}`}
style={{
width: px,
height: px,
position: 'relative',
}}
>
{/* Empty star (background) */}
<Ionicons
name="star"
size={px}
color={emptyColor}
style={{ position: 'absolute', top: 0, left: 0 }}
/>
{/* Filled star clipped to filledWidth */}
{filledRatio > 0 ? (
<View
style={{
position: 'absolute',
top: 0,
left: 0,
width: filledWidth,
height: px,
overflow: 'hidden',
}}
>
<Ionicons
name="star"
size={px}
color={filledColor}
style={{ position: 'absolute', top: 0, left: 0 }}
/>
</View>
) : null}
</View>
);
if (interactive) {
stars.push(
<TouchableOpacity
key={`press-${i}`}
onPress={() => onChange?.(i)}
hitSlop={4}
activeOpacity={0.7}
>
{star}
</TouchableOpacity>
);
} else {
stars.push(star);
}
}
return (
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 2 }}>
{stars}
</View>
);
}