Files
videogen/client/src/components/wahl.tsx
T
2026-08-15 13:43:52 +02:00

76 lines
2.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Pressable, StyleSheet, Text, View } from 'react-native';
import { colors, radius, space, text } from '@/theme/tokens';
type Option<T extends string> = { wert: T; titel: string; erklaerung: string };
type Props<T extends string> = {
label: string;
optionen: Option<T>[];
wert: T;
aufWahl: (w: T) => void;
};
/**
* Auswahl mit Erklärung je Option statt eines nackten Schalters.
*
* Bewusst so: „Zweck" und „Startwerte" beim Ordner entscheiden darüber, woraus
* später generiert wird – wer sie nicht versteht, baut sich einen Ordner, der
* nicht das tut, was er erwartet (konzept-bilder-feed.md §9).
*/
export function Wahl<T extends string>({ label, optionen, wert, aufWahl }: Props<T>) {
return (
<View style={s.block}>
<Text style={s.label}>{label}</Text>
<View style={s.optionen}>
{optionen.map((o) => {
const aktiv = o.wert === wert;
return (
<Pressable
key={o.wert}
onPress={() => aufWahl(o.wert)}
accessibilityRole="radio"
accessibilityState={{ selected: aktiv }}
style={({ pressed }) => [s.opt, aktiv ? s.optAktiv : null, pressed ? s.gedrueckt : null]}>
<View style={s.kopf}>
<View style={[s.punkt, aktiv ? s.punktAktiv : null]}>
{aktiv ? <View style={s.punktKern} /> : null}
</View>
<Text style={[s.titel, aktiv ? s.titelAktiv : null]}>{o.titel}</Text>
</View>
<Text style={s.erklaerung}>{o.erklaerung}</Text>
</Pressable>
);
})}
</View>
</View>
);
}
const s = StyleSheet.create({
block: { gap: space.sm },
label: { ...text.label, color: colors.mut },
optionen: { gap: space.sm },
opt: {
backgroundColor: colors.surface,
borderColor: colors.border,
borderWidth: StyleSheet.hairlineWidth,
borderRadius: radius.md,
padding: space.lg,
gap: space.sm,
},
optAktiv: { borderColor: colors.accent, backgroundColor: 'rgba(255,92,57,0.08)' },
gedrueckt: { opacity: 0.8 },
kopf: { flexDirection: 'row', alignItems: 'center', gap: space.md },
punkt: {
width: 18, height: 18, borderRadius: 9,
borderColor: colors.borderStrong, borderWidth: 1.5,
alignItems: 'center', justifyContent: 'center',
},
punktAktiv: { borderColor: colors.accent },
punktKern: { width: 9, height: 9, borderRadius: 5, backgroundColor: colors.accent },
titel: { ...text.body, fontWeight: '600', color: colors.txt },
titelAktiv: { color: colors.txt },
erklaerung: { ...text.label, color: colors.mut, lineHeight: 18 },
});