191 lines
5.6 KiB
TypeScript
191 lines
5.6 KiB
TypeScript
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';
|