Skip to content

Commit 04ec2a2

Browse files
committed
feat: add UploadButton component for file uploads
This commit introduces a new UploadButton component that allows users to upload files with error handling for file size limits. The component utilizes Radix UI for form control and includes a button with a loading state during uploads. It also provides user feedback for file size errors.
1 parent b6922a1 commit 04ec2a2

File tree

1 file changed

+69
-0
lines changed

1 file changed

+69
-0
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { Root, FormControl, FormField, FormMessage } from '@radix-ui/react-form'
2+
import { useRef, useState } from 'react'
3+
import { PlusIcon } from 'lucide-react'
4+
5+
import { ButtonBase as Button } from '../ui/button/button-base.tsx'
6+
import { formatFileSize } from '@/utils/format-file-size.ts'
7+
8+
interface UploadButtonProps {
9+
onUpload: (file: File) => void
10+
isUploading?: boolean
11+
accept?: string[]
12+
maxSize?: number
13+
}
14+
15+
export function UploadButton({
16+
onUpload,
17+
isUploading = false,
18+
accept = ['*'],
19+
maxSize = 200_000_000,
20+
}: UploadButtonProps) {
21+
const fileInputRef = useRef<HTMLInputElement>(null)
22+
const [error, setError] = useState<string | null>(null)
23+
24+
const handleButtonClick = () => {
25+
fileInputRef.current?.click()
26+
}
27+
28+
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
29+
const file = e.target.files?.[0]
30+
if (file) {
31+
onUpload(file)
32+
if (file.size > maxSize) {
33+
setError(`File is too large. Maximum size is ${formatFileSize(maxSize)}.`)
34+
if (fileInputRef.current) fileInputRef.current.value = ''
35+
return
36+
}
37+
38+
setError(null)
39+
onUpload(file)
40+
if (fileInputRef.current) fileInputRef.current.value = ''
41+
}
42+
}
43+
44+
return (
45+
<Root className="inline-flex flex-col gap-2" onSubmit={(e) => e.preventDefault()}>
46+
<FormField name="file">
47+
<FormControl asChild>
48+
<input
49+
accept={accept.join(',')}
50+
className="hidden"
51+
multiple={false}
52+
onChange={handleFileChange}
53+
ref={fileInputRef}
54+
type="file"
55+
/>
56+
</FormControl>
57+
</FormField>
58+
<Button disabled={isUploading} loading={isUploading} onClick={handleButtonClick} type="button" variant="primary">
59+
<div className="flex items-center gap-2">
60+
<PlusIcon size={20} />
61+
<span>Add file</span>
62+
</div>
63+
</Button>
64+
<FormField name="file">
65+
<FormMessage className="text-sm text-red-500 mt-1">{error}</FormMessage>
66+
</FormField>
67+
</Root>
68+
)
69+
}

0 commit comments

Comments
 (0)