Skip to content

FilePicker

파일을 끌어다 놓거나, 눌러서 파일 창을 여는 점선 상자입니다.

tsx
import { FilePicker } from 'neba';

<FilePicker
  multiple
  label="첨부"
  accept="image/*,.pdf"
  maxSize={5_000_000}
  maxFiles={4}
  onFilesChange={setFiles}
  onReject={setRejected}
/>;

Props

Prop타입기본값설명
variant공통'solid' | 'outline' | 'text''outline'표면의 무게. 셋 다 점선 테두리는 공유합니다 — 라이브러리에서 실선이 아닌 선을 긋는 유일한 곳이고, 장식이 아니라 "여기에 놓을 수 있다"는 관례이기 때문입니다
size공통'xs' | 'sm' | 'md' | 'lg' | 'xl''md'상자의 여백과 글자 크기. 상자는 안에 쓰인 글이 아니라 받아 내야 할 제스처에 맞춰 커집니다
color공통'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'의미론적 색 역할. 임의 색상값은 받지 않습니다
density공통'default' | 'compact''default'여백만 바꿉니다. 높이와 글자 크기는 그대로
elevation공통0 | 1 | 2 | 30그림자 깊이. 0은 그림자 없음
acceptstring브라우저 파일 창이 보여 줄 종류 — 'image/*,.pdf'. 드롭된 파일도 같은 문자열로 검사합니다. 브라우저는 그렇게 하지 않습니다
multiplebooleanfalse파일을 여러 개 고를 수 있는지
maxSizenumber파일 하나의 최대 크기(바이트)
maxFilesnumber한 번에 들고 있을 수 있는 개수. 한 번의 드롭이 아니라 이미 들고 있는 것과 합쳐서 셉니다
valuereadonly File[]고른 파일들. controlled
defaultValuereadonly File[]처음 고른 파일들
onFilesChange(files: File[]) => void파일 목록이 바뀔 때
onReject(rejections: FileRejection[]) => void되돌려 보낸 파일과 그 이유. 없으면 거부된 파일이 조용히 사라지는데, 드롭존이 하는 가장 나쁜 일입니다
titleReactNode상자 안의 문장
hintReactNode그 아래 줄 — 무엇을, 얼마나 크게, 몇 개까지
iconReactNode문장 위의 글리프. null을 주면 그림 없는 상자가 됩니다
showListbooleantrue고른 파일들을 상자 아래에 나열하고 각각 지울 방법을 붙입니다
removeLabel(name: string) => string파일 삭제 버튼의 접근성 이름
labelReactNode컨트롤과 연결되는 라벨. Base UI Field가 묶어 줍니다
descriptionReactNode보조 설명
errorReactNode오류 메시지. 값이 있으면 invalid 상태도 함께 켜지고 색 계열이 danger로 넘어갑니다
invalidboolean!!error메시지 없이 invalid만 켭니다. 외부 폼 라이브러리가 유효성을 가질 때
readOnlybooleanfalse값은 보이지만 바꿀 수 없음. 색과 가장자리는 유지한 채 채도만 빠집니다
disabledbooleanfalse사용 불가. 색 계열을 버리고 중립 회색이 됩니다
fullWidthbooleantrue컨테이너 너비만큼 확장
requiredbooleanfalse폼 제출 전에 파일이 있어야 하는지
namestring폼 제출 시의 필드 이름

예시

Variant

셋 다 점선 테두리를 공유합니다. 라이브러리에서 실선이 아닌 선을 긋는 유일한 곳이고, 장식이 아닙니다 — 점선 사각형은 "이 영역은 드롭을 받는다"는 굳어진 표시이고, 카드처럼 생긴 드롭존은 아무도 드롭해 보지 않는 카드입니다.

되돌려 보낸 파일

상태

브라우저는 드롭된 파일에 accept를 적용하지 않습니다

accept 속성은 브라우저 자기 파일 창에만 걸립니다. 드래그로 도착한 파일은 그 문자열을 한 번도 통과한 적이 없습니다. 속성만 걸어 둔 드롭존은 드롭 앞에서는 무엇이든 받습니다.

이 컴포넌트는 같은 문자열을 직접 검사합니다 — 속성이 취하는 세 가지 형태 그대로: .확장자, type/subtype, type/*.

깜박이지 않는 드래그 상태

dragenterdragleave는 포인터가 존의 자식을 지날 때마다 발생합니다. 그래서 불리언 하나를 토글하는 드롭존은 파일이 자기 내용 위를 지나는 내내 깜박입니다. 세는 것이 그 해법이고, 내용이 든 존에서 살아남는 유일한 방법입니다.

maxFiles는 "이만큼 놓을 수 있다"가 아닙니다

"이만큼 갖고 있을 수 있다"입니다. 이미 든 파일과 합쳐서 세므로, 두 개를 들고 있는 maxFiles={3} 픽커에 세 개를 놓으면 하나만 받아들입니다.

왜 Base UI 프리미티브가 없는가

드롭존은 드래그 이벤트 네 개를 듣는 <div>와, 대신 눌러 주는 <input type="file">입니다. 위치를 잡을 팝업도, 가둘 포커스도, 돌아다닐 roving focus도 없습니다. 디자인 언어가 허용하는 폴백 — 평범한 React와 DOM — 이 여기서는 옳은 선택입니다.

남는 것은 손으로 만든 드롭존들이 대개 틀리는 부분들이고, 위의 세 절이 그것들입니다.

접근성

상자는 <div>이고 그 안의 누를 수 있는 영역이 진짜 <button>입니다. 파일 목록은 그 버튼 바깥에 있습니다 — 삭제 버튼들을 열기 버튼 안에 넣을 수 없기 때문이고, ChipListItem이 쓰는 것과 같은 구조입니다.

진짜 <input type="file">은 숨기지 않고 화면 밖으로 보냅니다. display: nonevisibility: hidden은 일부 브라우저에서 input을 포커스 불가로 만드는데, 이 input은 폼과 required 유효성 메시지에 여전히 닿을 수 있어야 합니다.

onReject를 붙이세요. 없으면 되돌려 보낸 파일이 아무 말 없이 사라집니다.

Released under the MIT License