fitst commit

This commit is contained in:
Samandar Turgunboyev
2026-01-28 18:26:50 +05:00
parent 166a55b1e9
commit 124798419b
196 changed files with 26627 additions and 421 deletions

View File

@@ -0,0 +1,190 @@
import { ThemedText } from '@/components/themed-text';
import Feather from '@expo/vector-icons/Feather';
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import React, { useEffect, useRef, useState } from 'react';
import { Animated, Modal, TouchableOpacity, View } from 'react-native';
import { styles } from '../styles/welcomeStyle';
export default function LanguageSelect() {
const [visible, setVisible] = useState(false);
const [current, setCurrent] = useState('uz');
const [menuPos, setMenuPos] = useState({ x: 0, y: 0, width: 0, height: 0 });
const triggerRef = useRef<View>(null);
useEffect(() => {
const fetchLang = async () => {
const lang = await getLang();
if (lang) setCurrent(lang);
};
fetchLang();
}, []);
const backdropAnim = useRef(new Animated.Value(0)).current;
const fadeAnim = useRef(new Animated.Value(0)).current;
const slideAnim = useRef(new Animated.Value(-20)).current;
const scaleAnim = useRef(new Animated.Value(0.9)).current;
const languages = [
{ key: 'uz', label: "🇺🇿 O'zbek" },
{ key: 'ru', label: '🇷🇺 Русский' },
{ key: 'en', label: '🇺🇸 English' },
];
const openMenu = () => {
triggerRef.current?.measureInWindow((x, y, width, height) => {
setMenuPos({
x,
y: y + height + 60,
width,
height,
});
setVisible(true);
});
};
const closeMenu = () => {
Animated.parallel([
Animated.timing(backdropAnim, {
toValue: 0,
duration: 200,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 0,
duration: 150,
useNativeDriver: true,
}),
Animated.timing(slideAnim, {
toValue: -20,
duration: 150,
useNativeDriver: true,
}),
Animated.timing(scaleAnim, {
toValue: 0.9,
duration: 150,
useNativeDriver: true,
}),
]).start(() => {
setVisible(false);
});
};
useEffect(() => {
if (visible) {
backdropAnim.setValue(0);
fadeAnim.setValue(0);
slideAnim.setValue(-20);
scaleAnim.setValue(0.9);
Animated.parallel([
Animated.timing(backdropAnim, {
toValue: 1,
duration: 250,
useNativeDriver: true,
}),
Animated.timing(fadeAnim, {
toValue: 1,
duration: 250,
useNativeDriver: true,
}),
Animated.spring(slideAnim, {
toValue: 0,
tension: 100,
friction: 8,
useNativeDriver: true,
}),
Animated.spring(scaleAnim, {
toValue: 1,
tension: 100,
friction: 8,
useNativeDriver: true,
}),
]).start();
}
}, [visible]);
const selectLanguage = async (lang: string) => {
closeMenu();
setCurrent(lang);
await i18n.changeLanguage(lang);
await saveLang(lang);
};
return (
<View>
<View style={styles.langContainer} ref={triggerRef}>
<TouchableOpacity style={styles.langTrigger} onPress={openMenu} activeOpacity={0.7}>
<Feather name="globe" size={18} color="black" />
<ThemedText style={{ ...styles.langText }}>{current.toUpperCase()}</ThemedText>
</TouchableOpacity>
<Modal
transparent
visible={visible}
animationType="none"
statusBarTranslucent
onRequestClose={closeMenu}
>
<TouchableOpacity style={{ flex: 1 }} activeOpacity={1} onPress={closeMenu}>
<Animated.View
style={{
...StyleSheet.absoluteFillObject,
opacity: backdropAnim,
}}
/>
<Animated.View
style={{
position: 'absolute',
left: menuPos.x - menuPos.width - 20,
top: menuPos.y,
width: 160,
backgroundColor: '#fff',
borderRadius: 16,
shadowColor: '#000',
shadowOffset: { width: 0, height: 8 },
shadowOpacity: 0.15,
shadowRadius: 16,
elevation: 12,
overflow: 'hidden',
opacity: fadeAnim,
transform: [{ translateY: slideAnim }, { scale: scaleAnim }],
}}
>
{languages.map((l, index) => (
<TouchableOpacity
key={l.key}
onPress={() => selectLanguage(l.key)}
activeOpacity={0.7}
style={{
paddingVertical: 14,
paddingHorizontal: 16,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
backgroundColor: current === l.key ? '#EEF2FF' : 'transparent',
borderBottomWidth: index < languages.length - 1 ? 1 : 0,
borderBottomColor: '#F3F4F6',
}}
>
<ThemedText
style={[styles.langItemText, current === l.key && styles.langItemTextActive]}
>
{l.label}
</ThemedText>
{current === l.key && <MaterialIcons name="done" size={20} color="#6366F1" />}
</TouchableOpacity>
))}
</Animated.View>
</TouchableOpacity>
</Modal>
</View>
</View>
);
}
// StyleSheet.absoluteFillObject uchun import qo'shing:
import { getLang, saveLang } from '@/hooks/storage.native';
import i18n from '@/i18n/i18n';
import { StyleSheet } from 'react-native';