File Dropzone
拖放上傳區:磁吸虛線邊框朝游標偏移、跑馬燈流動,檔案列表附環形進度。
載入預覽⋯
Playground
即時調整 props、程式碼片段同步更新——直接試出你要的樣子再複製。
0.12
<FileDropzone />
安裝
npx shadcn@latest add https://webberui.com/r/file-dropzone.json或在 components.json 設定 registries 後,改用 @webberui/file-dropzone 安裝。
安裝依賴後,從 registry JSON(/r/file-dropzone.json 的 files[0].content)複製 file-dropzone.tsx 原始碼到你的 components/ui/ 目錄:
npm install motion lucide-react clsx tailwind-merge使用
import { FileDropzone } from "@/components/ui/file-dropzone";
<FileDropzone
accept="image/*,.pdf"
maxFiles={4}
maxSize={10 * 1024 * 1024}
onFilesAdded={(files) => console.log(files)}
/>接上真實上傳
未提供 upload 時元件以模擬進度展示動畫;接上後端時傳入非同步函式並回報進度:
<FileDropzone
upload={async (file, onProgress) => {
await uploadToServer(file, {
onUploadProgress: (e) => onProgress(e.loaded / e.total),
});
}}
/>resolve 顯示成功打勾,reject 顯示失敗狀態。
Props
| Prop | 型別 | 預設值 | 說明 |
|---|---|---|---|
onFilesAdded | (files: File[]) => void | — | 檔案通過驗證並加入清單時觸發 |
upload | (file: File, onProgress: (p: number) => void) => Promise<void> | — | 自訂上傳邏輯,onProgress 回報 0–1;未提供時模擬進度 |
onRemove | (file: File) => void | — | 從清單移除檔案時觸發 |
onReject | (file: File, reason: "type" | "size" | "count") => void | — | 檔案未通過驗證時觸發 |
accept | string | — | 接受的檔案型別(同原生 accept),拖放也會套用驗證 |
multiple | boolean | true | 允許多檔;false 時新檔案取代舊檔案 |
maxFiles | number | — | 清單檔案數量上限 |
maxSize | number | — | 單一檔案大小上限(bytes) |
strength | number | 0.12 | 磁吸強度,邊框偏移量 = 游標相對中心距離 × strength |
label | string | "拖放檔案到這裡,或點擊選取" | 放置區主要文字 |
disabled | boolean | false | 停用整個放置區 |
細節
- 磁吸邊框:拖曳懸停時,虛線邊框以 spring 朝游標方向偏移(上限 10px)並輕微放大,同時虛線以跑馬燈方式流動
- 拖曳進出子元素會連發
dragenter/dragleave,內部以深度計數避免邊框狀態閃爍 - 進度環以 SVG
stroke-dashoffset搭配 spring 平滑推進,完成後彈出打勾、失敗顯示警示 accept驗證支援副檔名(.pdf)、萬用字元(image/*)與完整 MIME,拖放與點選皆套用- 檔案選取後會清空 input value,同一檔案可重複選取
可及性
- 放置區為
role="button",可 Tab 聚焦,Enter/Space 開啟檔案選取器 - 進度環帶
role="progressbar"與aria-valuenow,成功/失敗圖示有對應aria-label - 上傳中的檔案數量透過
aria-live="polite"播報給螢幕閱讀器 - 使用者系統開啟「減少動態效果」時,磁吸偏移、跑馬燈與進出場動畫停用,進度環直接跳至目前值