Files
ckeproekt/app/admin/components/ImageUpload.tsx

203 lines
6.5 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.

'use client';
/* eslint-disable @typescript-eslint/no-unused-vars */
import React, { useState, useRef } from 'react';
import { Upload, X, Image as ImageIcon, AlertCircle } from 'lucide-react';
interface ImageUploadProps {
value?: string;
onChange: (url: string) => void;
onRemove: () => void;
maxSize?: number; // in MB
acceptedTypes?: string[];
className?: string;
}
export default function ImageUpload({
value,
onChange,
onRemove,
maxSize = 5,
acceptedTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'],
className = ''
}: ImageUploadProps) {
const [isUploading, setIsUploading] = useState(false);
const [error, setError] = useState<string>('');
const [dragActive, setDragActive] = useState(false);
const fileInputRef = useRef<HTMLInputElement>(null);
const validateFile = (file: File): string | null => {
// Проверка типа файла
if (!acceptedTypes.includes(file.type)) {
return `Неподдерживаемый тип файла. Разрешены: ${acceptedTypes.join(', ')}`;
}
// Проверка размера файла
if (file.size > maxSize * 1024 * 1024) {
return `Файл слишком большой. Максимальный размер: ${maxSize} MB`;
}
return null;
};
const handleFileUpload = async (file: File) => {
const validationError = validateFile(file);
if (validationError) {
setError(validationError);
return;
}
setIsUploading(true);
setError('');
try {
// В реальном приложении здесь будет загрузка на сервер
// Для демонстрации используем FileReader для создания data URL
const reader = new FileReader();
reader.onload = (e) => {
const result = e.target?.result as string;
onChange(result);
setIsUploading(false);
};
reader.onerror = () => {
setError('Ошибка при загрузке файла');
setIsUploading(false);
};
reader.readAsDataURL(file);
} catch (error) {
setError('Ошибка при загрузке файла');
setIsUploading(false);
}
};
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0];
if (file) {
handleFileUpload(file);
}
};
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setDragActive(false);
const file = e.dataTransfer.files?.[0];
if (file) {
handleFileUpload(file);
}
};
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setDragActive(true);
};
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setDragActive(false);
};
const handleRemove = () => {
onRemove();
setError('');
if (fileInputRef.current) {
fileInputRef.current.value = '';
}
};
const openFileDialog = () => {
fileInputRef.current?.click();
};
return (
<div className={`space-y-4 ${className}`}>
{value ? (
// Предварительный просмотр изображения
<div className="relative group">
<img
src={value}
alt="Preview"
className="w-full h-64 object-cover rounded-lg border border-gray-200"
/>
{/* Overlay с действиями */}
<div className="absolute inset-0 bg-black bg-opacity-50 opacity-0 group-hover:opacity-100 transition-opacity duration-200 rounded-lg flex items-center justify-center">
<div className="flex space-x-2">
<button
type="button"
onClick={openFileDialog}
className="p-2 bg-white text-gray-800 rounded-full hover:bg-gray-100 transition-colors"
title="Заменить изображение"
>
<Upload className="h-4 w-4" />
</button>
<button
type="button"
onClick={handleRemove}
className="p-2 bg-red-500 text-white rounded-full hover:bg-red-600 transition-colors"
title="Удалить изображение"
>
<X className="h-4 w-4" />
</button>
</div>
</div>
</div>
) : (
// Область загрузки
<div
className={`border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
dragActive
? 'border-blue-500 bg-blue-50'
: 'border-gray-300 hover:border-gray-400'
} ${isUploading ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'}`}
onDrop={handleDrop}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onClick={!isUploading ? openFileDialog : undefined}
>
{isUploading ? (
<div className="flex flex-col items-center space-y-2">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
<p className="text-sm text-gray-600">Загрузка...</p>
</div>
) : (
<div className="flex flex-col items-center space-y-2">
<ImageIcon className="h-12 w-12 text-gray-400" />
<div className="text-sm text-gray-600">
<span className="font-medium text-blue-600">Нажмите для выбора</span> или перетащите файл сюда
</div>
<p className="text-xs text-gray-500">
PNG, JPG, GIF, WEBP до {maxSize} MB
</p>
</div>
)}
</div>
)}
{/* Скрытый input для выбора файла */}
<input
ref={fileInputRef}
type="file"
accept={acceptedTypes.join(',')}
onChange={handleInputChange}
className="hidden"
disabled={isUploading}
/>
{/* Сообщение об ошибке */}
{error && (
<div className="flex items-center space-x-2 text-red-600 text-sm">
<AlertCircle className="h-4 w-4" />
<span>{error}</span>
</div>
)}
{/* Информация о файле */}
{value && !error && (
<div className="text-xs text-gray-500">
<p>Изображение загружено успешно</p>
</div>
)}
</div>
);
}