Dropzone
Dropzone
드래그 앤 드롭으로 사용자에게서 파일을 받아요. @mantine/dropzone 패키지로 제공되며 MIT 라이선스로 배포돼요.
출처: 문서
본문
설치
yarn add @mantine/dropzone
설치 후 애플리케이션의 루트에서 패키지 스타일을 import 해요:
import '@mantine/core/styles.css';
// ‼️ dropzone 스타일은 core 패키지 스타일 다음에 import 하세요
import '@mantine/dropzone/styles.css';
사용법
Dropzone은 사용자에게서 하나 이상의 파일을 받을 수 있게 해줘요. 이 컴포넌트는 react-dropzone을 기반으로 하며 모든 핵심 기능을 지원해요:
-
제공된 mime types에 기반해 파일을 수락/거부해요
-
개별 파일 크기를 제한해요
-
주어진 children을 렌더링하고 현재 상태에 따라 요소를 표시하는 컨텍스트 기반 컴포넌트를 제공해요
import { Group, Text } from '@mantine/core';
import { UploadSimpleIcon, ImageIcon, XIcon } from '@phosphor-icons/react';
import { Dropzone, DropzoneProps, IMAGE_MIME_TYPE } from '@mantine/dropzone';
export function BaseDemo(props: Partial<DropzoneProps>) {
return (
<Dropzone
onDrop={(files) => console.log('accepted files', files)}
onReject={(files) => console.log('rejected files', files)}
maxSize={5 * 1024 ** 2}
accept={IMAGE_MIME_TYPE}
{...props}
>
<Group justify="center" gap="xl" mih={220} style={{ pointerEvents: 'none' }}>
<Dropzone.Accept>
<UploadSimpleIcon size="3.2rem" />
</Dropzone.Accept>
<Dropzone.Reject>
<XIcon size="3.2rem" />
</Dropzone.Reject>
<Dropzone.Idle>
<ImageIcon size="3.2rem" />
</Dropzone.Idle>
<div>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</div>
</Group>
</Dropzone>
);
}
Dropzone.Accept, Dropzone.Reject, Dropzone.Idle
Dropzone.Accept, Dropzone.Reject, Dropzone.Idle 컴포넌트는 사용자가 특정 동작을 수행할 때만 보여요:
-
Dropzone.Accept는 사용자가 수락할 수 있는 파일을 dropzone 위로 끌어올 때만 보여요 -
Dropzone.Reject는 사용자가 수락할 수 없는 파일을 dropzone 위로 끌어올 때만 보여요 -
Dropzone.Idle는 사용자가 dropzone 위로 아무것도 끌어올리지 않을 때 보여요
로딩 상태
LoadingOverlay 컴포넌트로 로딩 상태를 나타내려면 loading prop을 설정해요. loading prop이 true일 때 사용자는 새 파일을 드롭하거나 선택할 수 없어요(Dropzone이 비활성화됨):
import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone loading onDrop={() => {}}>
{/* children */}
</Dropzone>
);
}
비활성 상태
자신만의 로딩 상태를 구현하고 싶다면 LoadingOverlay 없이 Dropzone을 비활성화할 수 있어요. loading과 마찬가지로 Dropzone이 비활성화되면 사용자는 새 파일을 드롭하거나 선택할 수 없어요:
import { Dropzone } from '@mantine/dropzone';
import classes from './Demo.module.css';
function Demo() {
return (
<Dropzone disabled onDrop={() => {}}>
{/* children... */}
</Dropzone>
);
}
파일 브라우저 수동 열기
컴포넌트 밖에서 파일 브라우저를 열려면 openRef prop으로 파일 브라우저를 트리거할 함수를 얻어요:
import { useRef } from 'react';
import { Button, Group } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const openRef = useRef<() => void>(null);
return (
<>
<Dropzone openRef={openRef} onDrop={() => {}}>
{/* children */}
</Dropzone>
<Group justify="center" mt="md">
<Button onClick={() => openRef.current?.()}>Select files</Button>
</Group>
</>
);
}
자식 포인터 이벤트 활성화
기본적으로 Dropzone은 드래그 이벤트가 동작하도록 자식의 포인터 이벤트를 비활성화해요. activateOnClick={false}일 때 Dropzone 안의 어떤 자식을 클릭해도 아무 일도 일어나지 않아요. 하지만 pointerEvents: 'all' 스타일을 설정해 자식을 클릭 가능하게 만들 수 있어요. 이 스타일은 버튼이나 링크 같은 인터랙티브 요소에만 설정해야 한다는 점에 주의하세요.
import { useRef } from 'react';
import { Button, Group } from '@mantine/core';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const openRef = useRef<() => void>(null);
return (
<Dropzone onDrop={() => {}} activateOnClick={false}>
<Button
onClick={() => openRef.current?.()} style={{ pointerEvents: 'all' }}
>
Select files
</Button>
</Dropzone>
);
}
Mime types
파일 타입을 지정하려면 키가 mime type이고 값이 파일 확장자 배열인 객체를 제공해요. 특정 파일 타입을 수락하는 더 많은 예시는 react-dropzone 문서에서 찾을 수 있어요.
import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone
onDrop={() => {}}
accept={{ 'image/png': ['.png'], 'image/gif': ['.gif'] }}
>
{/* children */}
</Dropzone>
);
}
accept prop에 mime types 배열을 제공해 파일 타입을 지정할 수도 있어요:
import { Dropzone } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone onDrop={() => {}} accept={['image/png', 'image/gif']}>
{/* children */}
</Dropzone>
);
}
조사 시간을 아끼려면 @mantine/dropzone에서 내보내는 MIME_TYPES 변수를 사용할 수 있어요:
import { Dropzone, MIME_TYPES } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone
onDrop={() => {}}
accept={{
'image/png': ['.png'],
'image/gif': ['.gif'],
[MIME_TYPES.jpeg]: ['.jpg', '.jpeg'],
}}
>
{/* children */}
</Dropzone>
);
}
MIME_TYPES는 다음 데이터를 포함해요:
| Key | Mime type |
|---|---|
| png | image/png |
| gif | image/gif |
| jpeg | image/jpeg |
| svg | image/svg+xml |
| webp | image/webp |
| avif | image/avif |
| heic | image/heic |
| heif | image/heif |
| mp4 | video/mp4 |
| zip | application/zip |
| rar | application/x-rar |
| 7z | application/x-7z-compressed |
| csv | text/csv |
| application/pdf | |
| doc | application/msword |
| docx | application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| xls | application/vnd.ms-excel |
| xlsx | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| ppt | application/vnd.ms-powerpoint |
| pptx | application/vnd.openxmlformats-officedocument.presentationml.presentation |
| exe | application/vnd.microsoft.portable-executable |
추가로 그룹화된 mime types를 사용할 수 있어요:
| 변수 | Mime types |
|---|---|
| IMAGE_MIME_TYPE | image/png, image/gif, image/jpeg, image/svg+xml, image/webp, image/avif, image/heic, image/heif |
| PDF_MIME_TYPE | application/pdf |
| MS_WORD_MIME_TYPE | application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document |
| MS_EXCEL_MIME_TYPE | application/vnd.ms-excel, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet |
| MS_POWERPOINT_MIME_TYPE | application/vnd.ms-powerpoint, application/vnd.openxmlformats-officedocument.presentationml.presentation |
import { Dropzone, IMAGE_MIME_TYPE } from '@mantine/dropzone';
function Demo() {
return (
<Dropzone onDrop={() => {}} accept={IMAGE_MIME_TYPE}>
{/* children */}
</Dropzone>
);
}
Styles API
Dropzone 루트 요소는 현재 상태에 따라 스타일을 바꾸는 다음 data 속성을 가져요:
-
data-loading–loadingprop이true일 때 -
data-accept– 사용자가 수락할 수 있는 파일을 dropzone 위로 끌어올 때 -
data-reject– 사용자가 수락할 수 없는 파일을 dropzone 위로 끌어올 때 -
data-idle– 기본 상태 – 사용자가 dropzone 위로 어떤 파일도 끌어올리지 않을 때
import { Text } from '@mantine/core';
import { Dropzone, IMAGE_MIME_TYPE } from '@mantine/dropzone';
import classes from './Demo.module.css';
function Demo() {
return (
<Dropzone onDrop={() => {}} accept={IMAGE_MIME_TYPE} className={classes.root}>
Drop images here
</Dropzone>
);
}
.root {
&[data-accept] {
color: var(--mantine-color-text);
}
&[data-reject] {
color: var(--mantine-color-red-6);
}
}
이미지 미리보기
import { useState } from 'react';
import { Text, Image, SimpleGrid } from '@mantine/core';
import { Dropzone, IMAGE_MIME_TYPE, FileWithPath } from '@mantine/dropzone';
function Demo() {
const [files, setFiles] = useState<FileWithPath[]>([]);
const previews = files.map((file, index) => {
const imageUrl = URL.createObjectURL(file);
return <Image key={index} src={imageUrl} onLoad={() => URL.revokeObjectURL(imageUrl)} />;
});
return (
<div>
<Dropzone accept={IMAGE_MIME_TYPE} onDrop={setFiles}>
<Text ta="center">Drop images here</Text>
</Dropzone>
<SimpleGrid cols={{ base: 1, sm: 4 }} mt={files.length > 0 ? 'xl' : 0}>
{previews}
</SimpleGrid>
</div>
);
}
ref 가져오기
import { useEffect, useRef } from 'react';
import { Dropzone } from '@mantine/dropzone';
function Demo() {
const dropzoneRef = useRef<HTMLDivElement>(null);
useEffect(() => {
dropzoneRef.current?.focus();
}, []);
return (
<Dropzone onDrop={() => {}} ref={dropzoneRef}>
{/* children */}
</Dropzone>
);
}
Dropzone.FullScreen 컴포넌트
Dropzone.FullScreen은 특정 영역 대신 브라우저 창에 드롭된 파일을 받을 수 있게 해줘요. Dropzone 컴포넌트와 같은 props를 지원해요.
import { useState } from 'react';
import { Group, Text, Button } from '@mantine/core';
import { UploadSimpleIcon, ImageIcon, XIcon } from '@phosphor-icons/react';
import { Dropzone, IMAGE_MIME_TYPE } from '@mantine/dropzone';
function Demo() {
const [active, setActive] = useState(false);
return (
<>
<Group justify="center">
<Button onClick={() => setActive((d) => !d)}>
{active ? 'Deactivate' : 'Activate'} full screen dropzone
</Button>
</Group>
<Dropzone.FullScreen
active={active}
accept={IMAGE_MIME_TYPE}
onDrop={(files) => {
console.log(files);
setActive(false);
}}
>
<Text size="xl" inline>
Drag images here or click to select files
</Text>
<Text size="sm" c="dimmed" inline mt={7}>
Attach as many files as you like, each file should not exceed 5mb
</Text>
</Dropzone.FullScreen>
</>
);
}