업로드
파일을 끌어 놓거나 버튼으로 골라 올리는 자리와, 올라가는 파일의 줄입니다.
놓을 자리는 파인 홈입니다. 끌어 오는 동안 놓을 수 있는지를 놓기 전에 알리고, 끌기는 지름길일 뿐이라 언제나 [파일 고르기] 버튼이 있습니다. 형식 · 크기 · 개수 제한은 고르기 전에 한 줄로 보이고, 거절은 어느 파일이 왜인지 한 줄씩 말합니다. 전부 거절이면 오류(bad), 일부만 받으면 주의(warn) 알림입니다. 전송은 앱이 하고, 컴포넌트는 파일을 건네고 상태를 받아 그립니다.
- import
- import { Dropzone, DropOverlay, FileList, FileItem } from "@uyoolabs/ui/ui";
- 쓰는 경우
- 폼 · 시트의 첨부 칸(Field 와 함께), 프로필 사진 · 로고 하나(multiple={false}), 문서 · 채널처럼 넓은 화면의 화면 전체 놓기(DropOverlay)에 씁니다. 큰 파일은 진행률 · 남은 시간 · 취소를 주고, 실패한 파일은 줄에 남겨 [다시 시도]를 둡니다.
- 쓰지 않는 경우
- 버튼 없이 끌기로만 올리는 영역을 만들지 않습니다(키보드 · 터치에서 막힙니다). 고른 뒤에야 제한을 알리지 않습니다. 실패한 파일을 조용히 목록에서 빼지 않습니다. 진행률 · 거절을 토스트로만 보이지 않습니다. '올리는 중' · '빼기' 에 빨강을 쓰지 않습니다. 파일 이름 끝(확장자)을 자르지 않습니다.
- 붙여넣기
- Dropzone 은 [파일 고르기]에 포커스가 있을 때 ⌘V · Ctrl+V 로 클립보드의 파일을 받습니다(acceptPaste, 기본 켬). DropOverlay 는 acceptPaste 를 켜면 입력 칸 · 편집기 밖에 붙여넣은 파일만 받습니다.
- 알림
- FileList 는 role=status 하나로 하나 끝날 때('3개 중 2개를 올렸습니다') · 실패할 때만 알립니다. 진행률 숫자는 읽지 않습니다. 거절 알림은 전부 거절이면 role=alert, 일부만 받으면 role=status 입니다.
첨부 칸
Field + Dropzone sm + FileList. 파일을 골라 보면 줄이 차례로 올라갑니다.
파일을 끌어 놓거나 고릅니다
PNG · JPG · PDF · 파일당 25MB · 5개까지(0/5)
이름에 '실패' 가 든 파일은 중간에 끊긴 것으로 보입니다.
상태
쉼 · 비활성(이유) · 오류(Field) · 한 줄 sm. hover 는 경계만 짙어집니다.
파일을 여기로 끌어 놓습니다
PNG · PDF · 파일당 25MB
파일을 여기로 끌어 놓습니다
보관된 프로젝트입니다
파일을 여기로 끌어 놓습니다
계약서 사본을 올립니다.
파일을 끌어 놓거나 고릅니다
파일당 25MB
화면 전체 놓기
DropOverlay. 덮개는 불투명한 primary 틴트 면 + 2px 점선이고 시각 전용입니다. 같은 일을 화면 안의 첨부 버튼이 합니다.
이미지 격자
layout='grid'. 진행 · 실패 · 대기는 사진 아래 뜬 띠에 얹습니다.
- 화이트보드.jpg
- 표지.png
- 스케치.png
- 메모.png
키보드
| 키 | 동작 |
|---|---|
| Tab | [파일 고르기] 버튼 → 목록의 줄 동작(취소 · 다시 시도 · 빼기) 순서입니다. 영역 자체는 탭 정지가 아닙니다. |
| EnterSpace | 버튼에서 파일 대화상자를 엽니다. 한 · 일 입력의 조합 중 Enter 는 열지 않습니다. |
| Esc | 끌어 오는 중이면 덮개를 거둡니다. |
| ⌘VCtrl+V | 버튼에 포커스가 있으면 클립보드의 파일을 받습니다. |
| Enter | 줄의 동작 버튼을 누릅니다. 줄을 빼면 포커스는 다음 줄의 같은 동작 → 이전 줄 → [파일 고르기] 로 갑니다. |
props
소스의 타입과 JSDoc 에서 생성한 표입니다. 컴포넌트를 고치면 pnpm gen 으로 다시 만듭니다.
Dropzone
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| accept | string[] | — | MIME 또는 확장자. ["image/png", "application/pdf", ".hwp"]. image/* 도 받는다. 없으면 모두 받는다 |
| maxSize | number | — | 바이트. 파일당 상한 |
| maxFiles | number | — | 올릴 수 있는 전체 개수. 이미 올린 수는 currentCount 로 받아 남은 자리만 받는다 |
| currentCount | number | — | 이미 올린 개수. 안내에 '(8/10)' 으로 보인다 |
| multiple | boolean | true | 기본 true. false 면 하나만 받는다(프로필 사진 · 로고) |
| onFiles필수 | (accepted: File[], rejected: RejectedFile[]) => void | — | 고르거나 놓거나 붙여넣을 때마다. 거른 결과를 둘로 나눠 준다. 거절이 있으면 컴포넌트가 거절 알림을 그린다 |
| disabled | boolean | false | |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<DropzoneLabels> | — | |
| disabledReason | ReactNode | — | 비활성 이유 한 줄. 안내 자리에 보이고 버튼의 aria-describedby 로 읽힌다 |
| invalid | boolean | — | Field 의 오류 상태. 경계를 오류 색으로 두고 버튼에 aria-invalid 를 단다 |
| size | "md" | "sm" | "md" | 기본 md. sm 은 한 줄(높이 52, 폼 칸 · 댓글 아래) |
| title | ReactNode | — | 영역 문구를 바꿀 때. 기본은 labels.title(sm 은 labels.titleInline) |
| hint | ReactNode | — | 제한 안내. 없으면 accept · maxSize · maxFiles 로 만든다('PNG · JPG · PDF · 파일당 25MB · 10개까지(8/10)') |
| icon | ReactNode | — | 기본은 올리기 아이콘(sm 은 클립) |
| acceptPaste | boolean | true | 버튼에 포커스가 있을 때 ⌘V · Ctrl+V 로 클립보드의 파일을 받는다. 기본 true(W3 결정 #16) |
| id | string | — | 버튼 id. Field 가 label · 도움말 · 오류를 잇는다 |
| aria-describedby | string | — | |
| aria-invalid | boolean | "true" | "false" | — | |
| aria-required | boolean | "true" | "false" | — | Field 가 넘기는 값. 버튼에는 aria-required 를 달 수 없어 쓰지 않는다 |
| className | string | — |
DropOverlay
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| accept | string[] | — | MIME 또는 확장자. ["image/png", "application/pdf", ".hwp"]. image/* 도 받는다. 없으면 모두 받는다 |
| maxSize | number | — | 바이트. 파일당 상한 |
| maxFiles | number | — | 올릴 수 있는 전체 개수. 이미 올린 수는 currentCount 로 받아 남은 자리만 받는다 |
| currentCount | number | — | 이미 올린 개수. 안내에 '(8/10)' 으로 보인다 |
| multiple | boolean | — | 기본 true. false 면 하나만 받는다(프로필 사진 · 로고) |
| onFiles필수 | (accepted: File[], rejected: RejectedFile[]) => void | — | 고르거나 놓거나 붙여넣을 때마다. 거른 결과를 둘로 나눠 준다. 거절이 있으면 컴포넌트가 거절 알림을 그린다 |
| disabled | boolean | — | |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<DropzoneLabels> | — | |
| target필수 | string | — | 놓을 곳 이름. '‘3분기 계획’ 문서' → '놓으면 ‘3분기 계획’ 문서에 첨부합니다' |
| title | ReactNode | — | 덮개 문장을 바꿀 때. 기본은 labels.overlayTitle(target) |
| hint | ReactNode | — | 덮개 아래 줄을 바꿀 때. 기본은 labels.overlayHint(파일 수) |
| acceptPaste | boolean | false | 화면(입력 칸 밖)에 붙여넣은 파일을 받는다. 기본 false — 편집기 · 입력 칸의 붙여넣기를 가로채지 않게 |
| className | string | — | |
| children필수 | ReactNode | — | 놓을 자리가 되는 화면(문서 · 채널) |
FileList
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| layout | "list" | "grid" | "list" | 기본 list. grid 는 이미지 썸네일 120 격자 |
| total | number | — | 전체 파일 수. done 과 함께 주면 하나 끝날 때마다 '3개 중 2개를 올렸습니다' 를 알린다 |
| done | number | — | 올리기를 마친 파일 수 |
| lang | "ko" | "en" | "ja" | — | |
| labels | Partial<FileListLabels> | — | |
| children필수 | ReactNode | — | <FileItem> 들 |
이 밖에 <ul> props(children 제외)를 그대로 받습니다.
FileItem
| prop | 타입 | 기본값 | 설명 |
|---|---|---|---|
| name필수 | string | — | 파일 이름. 길면 가운데를 줄이고 확장자 · 판 번호가 든 끝은 남긴다 |
| size | number | — | 바이트. 표시는 lang 에 맞춘 단위(4.2MB · 4.2 MB) |
| status필수 | "queued" | "uploading" | "done" | "error" | — | |
| progress | number | 0 | 0–100. uploading 일 때 |
| remaining | number | — | 남은 시간(초). 주면 '약 4초 남음' |
| error | ReactNode | — | status="error" 의 이유 한 문장. 없으면 labels.failed |
| thumbnail | string | ReactNode | — | 이미지 미리보기 URL 또는 노드. 없으면 확장자 아이콘 |
| onCancel | () => void | — | queued · uploading 의 취소 |
| onRetry | () => void | — | error 의 다시 시도 |
| onRemove | () => void | — | done · error 의 빼기 |
| lang | "ko" | "en" | "ja" | — | 없으면 FileList 의 lang |
| labels | Partial<FileListLabels> | — | |
| className | string | — |