mirror of
https://github.com/perfect-panel/ppanel-web.git
synced 2026-02-13 11:51:12 -05:00
115 lines
2.9 KiB
TypeScript
115 lines
2.9 KiB
TypeScript
import { Input } from '@workspace/ui/components/input';
|
|
import { Label } from '@workspace/ui/components/label';
|
|
import { cn } from '@workspace/ui/lib/utils';
|
|
import { Upload } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
|
|
type ReturnType = 'base64' | 'file';
|
|
|
|
interface UploadImageProps {
|
|
onChange: (value: string | File) => void;
|
|
returnType?: ReturnType;
|
|
id?: string;
|
|
children?: React.ReactNode;
|
|
className?: string;
|
|
maxSize?: number; // Maximum file size in MB
|
|
}
|
|
|
|
export const UploadImage = ({
|
|
onChange,
|
|
returnType = 'base64',
|
|
id = 'image-upload',
|
|
children,
|
|
className,
|
|
maxSize = 1,
|
|
}: UploadImageProps) => {
|
|
const [isDragging, setIsDragging] = useState(false);
|
|
|
|
const toBase64 = (file: File): Promise<string> => {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = () => resolve(reader.result as string);
|
|
reader.onerror = (error) => reject(error);
|
|
});
|
|
};
|
|
|
|
const validateFileSize = (file: File): boolean => {
|
|
const maxSizeInBytes = maxSize * 1024 * 1024;
|
|
if (file.size > maxSizeInBytes) {
|
|
alert(`File size exceeds the limit (${maxSize}MB)`);
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
const handleImageUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
const file = e.target.files?.[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
if (!validateFileSize(file)) return;
|
|
|
|
if (returnType === 'base64') {
|
|
const base64 = await toBase64(file);
|
|
onChange(base64);
|
|
} else {
|
|
onChange(file);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
const handleDragOver = (e: React.DragEvent<HTMLLabelElement>) => {
|
|
e.preventDefault();
|
|
setIsDragging(true);
|
|
};
|
|
|
|
const handleDragLeave = (e: React.DragEvent<HTMLLabelElement>) => {
|
|
e.preventDefault();
|
|
setIsDragging(false);
|
|
};
|
|
|
|
const handleDrop = async (e: React.DragEvent<HTMLLabelElement>) => {
|
|
e.preventDefault();
|
|
setIsDragging(false);
|
|
|
|
const file = e.dataTransfer.files?.[0];
|
|
if (!file) return;
|
|
|
|
try {
|
|
if (!validateFileSize(file)) return;
|
|
|
|
if (returnType === 'base64') {
|
|
const base64 = await toBase64(file);
|
|
onChange(base64);
|
|
} else {
|
|
onChange(file);
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<Input type='file' accept='image/*' className='hidden' id={id} onChange={handleImageUpload} />
|
|
<Label
|
|
htmlFor={id}
|
|
onDragOver={handleDragOver}
|
|
onDragLeave={handleDragLeave}
|
|
onDrop={handleDrop}
|
|
className={cn(
|
|
'cursor-pointer',
|
|
!children && 'flex items-center justify-center rounded-lg border-2 border-dashed p-4',
|
|
isDragging && 'border-primary bg-muted/50',
|
|
className,
|
|
)}
|
|
>
|
|
{children || <Upload />}
|
|
</Label>
|
|
</>
|
|
);
|
|
};
|