152 lines
3.9 KiB
TypeScript
152 lines
3.9 KiB
TypeScript
import { useTheme } from '@/components/ThemeContext';
|
|
import {
|
|
BottomSheetBackdrop,
|
|
BottomSheetBackdropProps,
|
|
BottomSheetModal,
|
|
BottomSheetScrollView,
|
|
} from '@gorhom/bottom-sheet';
|
|
import { Image } from 'expo-image';
|
|
import React, { useCallback, useEffect, useMemo, useRef } from 'react';
|
|
import { useTranslation } from 'react-i18next';
|
|
import { StyleSheet, Text, TouchableOpacity, View } from 'react-native';
|
|
|
|
export type Option = {
|
|
label: string;
|
|
value: string;
|
|
flag?: string;
|
|
};
|
|
|
|
type CategorySelectorProps = {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
selectedValue: string;
|
|
onSelect: (value: string) => void;
|
|
data: Option[];
|
|
};
|
|
|
|
export default function CategorySelectorBottomSheet({
|
|
isOpen,
|
|
onClose,
|
|
selectedValue,
|
|
onSelect,
|
|
data = [],
|
|
}: CategorySelectorProps) {
|
|
const { isDark } = useTheme();
|
|
const { t } = useTranslation();
|
|
const theme = {
|
|
background: isDark ? '#1e293b' : '#ffffff',
|
|
text: isDark ? '#f8fafc' : '#0f172a',
|
|
border: isDark ? '#334155' : '#e2e8f0',
|
|
selectedBg: '#2563eb',
|
|
selectedText: '#ffffff',
|
|
indicator: isDark ? '#cbd5e1' : '#94a3b8',
|
|
};
|
|
|
|
const bottomSheetRef = useRef<BottomSheetModal>(null);
|
|
const snapPoints = useMemo(() => ['60%', '85%'], []);
|
|
|
|
const renderBackdrop = useCallback(
|
|
(props: BottomSheetBackdropProps) => (
|
|
<BottomSheetBackdrop {...props} disappearsOnIndex={-1} appearsOnIndex={0} opacity={0.5} />
|
|
),
|
|
[]
|
|
);
|
|
|
|
useEffect(() => {
|
|
if (isOpen) {
|
|
bottomSheetRef.current?.present();
|
|
} else {
|
|
bottomSheetRef.current?.dismiss();
|
|
}
|
|
}, [isOpen]);
|
|
|
|
return (
|
|
<BottomSheetModal
|
|
ref={bottomSheetRef}
|
|
index={0}
|
|
snapPoints={snapPoints}
|
|
backdropComponent={renderBackdrop}
|
|
enablePanDownToClose
|
|
onDismiss={onClose}
|
|
handleIndicatorStyle={{ backgroundColor: theme.indicator, width: 50 }}
|
|
backgroundStyle={{
|
|
backgroundColor: theme.background,
|
|
borderTopLeftRadius: 20,
|
|
borderTopRightRadius: 20,
|
|
}}
|
|
>
|
|
<View style={[styles.header, { borderBottomColor: theme.border }]}>
|
|
<Text style={[styles.title, { color: theme.text }]}>{t('Tanlang')}</Text>
|
|
</View>
|
|
|
|
<BottomSheetScrollView style={styles.content} contentContainerStyle={styles.contentContainer}>
|
|
{data.map((item) => (
|
|
<TouchableOpacity
|
|
key={item.value}
|
|
style={[
|
|
styles.optionRow,
|
|
{ borderBottomColor: theme.border },
|
|
selectedValue === item.value && { backgroundColor: theme.selectedBg },
|
|
]}
|
|
onPress={() => {
|
|
onSelect(item.value);
|
|
onClose();
|
|
}}
|
|
>
|
|
{item.flag ? (
|
|
<Image
|
|
source={{ uri: `https://flagcdn.com/24x18/${item.flag.toLowerCase()}.png` }}
|
|
style={{ width: 24, height: 18, borderRadius: 2 }}
|
|
/>
|
|
) : null}
|
|
<Text
|
|
style={[
|
|
styles.optionText,
|
|
{ color: selectedValue === item.value ? theme.selectedText : theme.text },
|
|
selectedValue === item.value && styles.optionTextSelected,
|
|
]}
|
|
>
|
|
{item.label}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
))}
|
|
</BottomSheetScrollView>
|
|
</BottomSheetModal>
|
|
);
|
|
}
|
|
|
|
const styles = StyleSheet.create({
|
|
header: {
|
|
paddingVertical: 16,
|
|
paddingHorizontal: 20,
|
|
borderBottomWidth: 1,
|
|
alignItems: 'center',
|
|
},
|
|
title: {
|
|
fontSize: 18,
|
|
fontWeight: '700',
|
|
},
|
|
content: {
|
|
flex: 1,
|
|
},
|
|
contentContainer: {
|
|
paddingHorizontal: 8,
|
|
paddingBottom: 40,
|
|
},
|
|
optionRow: {
|
|
paddingVertical: 16,
|
|
paddingHorizontal: 20,
|
|
borderBottomWidth: 1,
|
|
flexDirection: 'row',
|
|
alignContent: 'center',
|
|
alignItems: 'center',
|
|
gap: 10,
|
|
},
|
|
optionText: {
|
|
fontSize: 16,
|
|
},
|
|
optionTextSelected: {
|
|
fontWeight: '600',
|
|
},
|
|
});
|