Initial commit
This commit is contained in:
224
src/components/FileDrop.tsx
Normal file
224
src/components/FileDrop.tsx
Normal file
@@ -0,0 +1,224 @@
|
||||
import React, { useCallback, useMemo, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import {
|
||||
Alert,
|
||||
Image,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native';
|
||||
import {
|
||||
ImagePickerResponse,
|
||||
launchImageLibrary,
|
||||
MediaType,
|
||||
} from 'react-native-image-picker';
|
||||
import Feather from 'react-native-vector-icons/Feather';
|
||||
|
||||
interface FileData {
|
||||
uri: string;
|
||||
name: string;
|
||||
type: string;
|
||||
base64: string;
|
||||
}
|
||||
|
||||
interface SingleFileDropProps {
|
||||
title: string;
|
||||
onFileSelected?: (file: FileData) => void;
|
||||
}
|
||||
|
||||
const SingleFileDrop: React.FC<SingleFileDropProps> = ({
|
||||
title,
|
||||
onFileSelected,
|
||||
}) => {
|
||||
const [selectedImage, setSelectedImage] = useState<string | null>(null);
|
||||
const { t } = useTranslation();
|
||||
const imagePickerOptions = useMemo(
|
||||
() => ({
|
||||
mediaType: 'photo' as MediaType,
|
||||
includeBase64: true,
|
||||
}),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleImagePickerResponse = useCallback(
|
||||
(response: ImagePickerResponse) => {
|
||||
if (response.didCancel) return; // foydalanuvchi bekor qilsa
|
||||
if (response.errorCode) {
|
||||
Alert.alert('Xato', response.errorMessage || 'Rasmni yuklashda xato');
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.assets && response.assets[0]) {
|
||||
const asset = response.assets[0];
|
||||
if (!asset.uri || !asset.type || !asset.base64) return;
|
||||
|
||||
// faqat PNG fayllarni qabul qilish
|
||||
if (asset.type !== 'image/png') {
|
||||
Alert.alert('Xato', 'Faqat PNG fayllarni yuklashingiz mumkin');
|
||||
return;
|
||||
}
|
||||
|
||||
setSelectedImage(asset.uri);
|
||||
|
||||
const fileData: FileData = {
|
||||
uri: asset.uri,
|
||||
name: asset.fileName || 'image.png',
|
||||
type: asset.type,
|
||||
base64: `data:${asset.type};base64,${asset.base64}`,
|
||||
};
|
||||
|
||||
onFileSelected?.(fileData);
|
||||
}
|
||||
},
|
||||
[onFileSelected],
|
||||
);
|
||||
|
||||
const openGallery = useCallback((): void => {
|
||||
launchImageLibrary(imagePickerOptions, handleImagePickerResponse);
|
||||
}, [imagePickerOptions, handleImagePickerResponse]);
|
||||
|
||||
const UploadIcon = useMemo(
|
||||
() => () =>
|
||||
(
|
||||
<View style={styles.iconContainer}>
|
||||
<View style={styles.downloadIcon}>
|
||||
<Feather name="download" color="#28A7E8" size={35} />
|
||||
</View>
|
||||
</View>
|
||||
),
|
||||
[],
|
||||
);
|
||||
|
||||
const renderContent = useMemo(() => {
|
||||
if (selectedImage) {
|
||||
return (
|
||||
<Image source={{ uri: selectedImage }} style={styles.previewImage} />
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<View style={styles.innerContainer}>
|
||||
<View style={styles.topContent}>
|
||||
{selectedImage ? (
|
||||
<Image
|
||||
source={{ uri: selectedImage }}
|
||||
style={styles.previewImage}
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<UploadIcon />
|
||||
<Text style={styles.sectionTitle}>{title}</Text>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View style={styles.bottomContent}>
|
||||
<View style={styles.dividerContainer}>
|
||||
<View style={styles.dividerLine} />
|
||||
<Text style={styles.orDividerText}>OR</Text>
|
||||
<View style={styles.dividerLine} />
|
||||
</View>
|
||||
<View style={styles.browseButton}>
|
||||
<Text style={styles.browseButtonText}>{t('Faylni yuklang')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
);
|
||||
}, [selectedImage, title, UploadIcon]);
|
||||
|
||||
return (
|
||||
<TouchableOpacity style={styles.dropSection} onPress={openGallery}>
|
||||
{renderContent}
|
||||
</TouchableOpacity>
|
||||
);
|
||||
};
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
dropSection: {
|
||||
borderWidth: 2,
|
||||
borderColor: '#28A7E8',
|
||||
borderStyle: 'dashed',
|
||||
borderRadius: 8,
|
||||
padding: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
minHeight: 200,
|
||||
flex: 1,
|
||||
},
|
||||
iconContainer: {
|
||||
marginBottom: 15,
|
||||
},
|
||||
downloadIcon: {
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 20,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
iconText: {
|
||||
fontSize: 20,
|
||||
color: '#007bff',
|
||||
fontWeight: 'bold',
|
||||
},
|
||||
sectionTitle: {
|
||||
fontSize: 16,
|
||||
fontWeight: '500',
|
||||
color: '#333',
|
||||
marginBottom: 10,
|
||||
},
|
||||
browseButton: {
|
||||
borderWidth: 1,
|
||||
borderColor: '#28A7E8',
|
||||
borderRadius: 8,
|
||||
padding: 8,
|
||||
},
|
||||
browseButtonText: {
|
||||
color: '#28A7E8',
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
},
|
||||
previewImage: {
|
||||
width: '100%',
|
||||
height: 150,
|
||||
borderRadius: 8,
|
||||
resizeMode: 'cover',
|
||||
},
|
||||
dividerContainer: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
marginVertical: 16,
|
||||
position: 'relative',
|
||||
flexDirection: 'row',
|
||||
},
|
||||
dividerLine: {
|
||||
width: 30,
|
||||
height: 2,
|
||||
backgroundColor: '#E7E7E7',
|
||||
},
|
||||
orDividerText: {
|
||||
paddingHorizontal: 12,
|
||||
fontSize: 14,
|
||||
color: '#999',
|
||||
zIndex: 1,
|
||||
},
|
||||
innerContainer: {
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
width: '100%',
|
||||
},
|
||||
|
||||
topContent: {
|
||||
alignItems: 'center',
|
||||
},
|
||||
|
||||
bottomContent: {
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
},
|
||||
});
|
||||
|
||||
export default SingleFileDrop;
|
||||
Reference in New Issue
Block a user