Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/components/upload/drag-n-drop.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Root } from '@radix-ui/react-form'
import { useState } from 'react'
import { MAX_FILE_SIZE } from '@/constants/files.ts'
import { FilePicker } from '../file-picker/index.tsx'
import { ButtonBase as Button } from '../ui/button/button-base.tsx'

Expand Down Expand Up @@ -32,7 +33,7 @@ export default function DragNDrop({ onFileSelected, onUpload, isUploading }: Dra
<Root className="space-y-6" onSubmit={(e) => e.preventDefault()}>
<FilePicker
file={file}
maxSize={200_000_000}
maxSize={MAX_FILE_SIZE}
onChange={(file) => {
setFile(file)
if (file && onFileSelected) {
Expand Down
80 changes: 80 additions & 0 deletions src/components/upload/upload-button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { FormControl, FormField, FormMessage, Root } from '@radix-ui/react-form'
import { PlusIcon } from 'lucide-react'
import { useRef, useState } from 'react'
import { MAX_FILE_SIZE } from '@/constants/files.ts'
import { formatFileSize } from '@/utils/format-file-size.ts'
import { ButtonBase as Button } from '../ui/button/button-base.tsx'

interface UploadButtonProps {
onUpload: (file: File) => void
isUploading?: boolean
accept?: string[]
maxSize?: number
}

export function UploadButton({
onUpload,
isUploading = false,
accept = ['*'],
maxSize = MAX_FILE_SIZE,
}: UploadButtonProps) {
const fileInputRef = useRef<HTMLInputElement>(null)
const [error, setError] = useState<string | null>(null)

function handleButtonClick() {
fileInputRef.current?.click()
}

function handleFileChange(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0]
if (file) {
if (file.size > maxSize) {
setError(`File is too large. Maximum size is ${formatFileSize(maxSize)}.`)
if (fileInputRef.current) fileInputRef.current.value = ''
return
}

setError(null)
onUpload(file)
if (fileInputRef.current) fileInputRef.current.value = ''
}
}

return (
<Root className="flex flex-col gap-2 items-end" onSubmit={(e) => e.preventDefault()}>
<FormField name="file">
<FormControl asChild>
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
Comment on lines +47 to +53
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe if you pass size here, the user will only be able to pick files with an appropriate size, hence rendering the use setError(File is too large. Maximum size is ${formatFileSize(maxSize)}.) unnecessary.

To be tested, maybe both are needed!

Suggested change
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
<input
accept={accept.join(',')}
className="hidden"
multiple={false}
onChange={handleFileChange}
ref={fileInputRef}
type="file"
size={maxSize}

https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input/file#size

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like that section is under “Getting information on selected files,” which is all about the File API, not HTML attribute? 👀

/>
</FormControl>
</FormField>
<div className="w-content">
<Button
disabled={isUploading}
loading={isUploading}
onClick={handleButtonClick}
type="button"
variant="primary"
>
<div className="flex items-center gap-2">
<PlusIcon size={20} />
<span>Add file</span>
</div>
</Button>
</div>
<div className="h-3">
{error && (
<FormField name="file">
<FormMessage className="text-sm text-red-500 mt-1 break-words max-w-full">{error}</FormMessage>
</FormField>
)}
</div>
</Root>
)
}
1 change: 1 addition & 0 deletions src/constants/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const MAX_FILE_SIZE = 200_000_000