This commit is contained in:
Iliyan Angelov
2025-09-14 23:24:25 +03:00
commit c67067a2a4
71311 changed files with 6800714 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
{
"globals": {
"useDropzone": true
}
}

View File

@@ -0,0 +1,144 @@
By providing `accept` prop you can make the dropzone accept specific file types and reject the others.
The value must be an object with a common [MIME type](https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types) as keys and an array of file extensions as values (similar to [showOpenFilePicker](https://developer.mozilla.org/en-US/docs/Web/API/window/showOpenFilePicker)'s types `accept` option).
```js static
useDropzone({
accept: {
'image/png': ['.png'],
'text/html': ['.html', '.htm'],
}
})
```
For more information see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input.
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Accept(props) {
const {
acceptedFiles,
fileRejections,
getRootProps,
getInputProps
} = useDropzone({
accept: {
'image/jpeg': [],
'image/png': []
}
});
const acceptedFileItems = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
const fileRejectionItems = fileRejections.map(({ file, errors }) => (
<li key={file.path}>
{file.path} - {file.size} bytes
<ul>
{errors.map(e => (
<li key={e.code}>{e.message}</li>
))}
</ul>
</li>
));
return (
<section className="container">
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
<em>(Only *.jpeg and *.png images will be accepted)</em>
</div>
<aside>
<h4>Accepted files</h4>
<ul>{acceptedFileItems}</ul>
<h4>Rejected files</h4>
<ul>{fileRejectionItems}</ul>
</aside>
</section>
);
}
<Accept />
```
### Browser limitations
Because of HTML5 File API differences across different browsers during the drag, Dropzone might not be able to detect whether the files are accepted or rejected in Safari nor IE.
Furthermore, at this moment it's not possible to read file names (and thus, file extensions) during the drag operation. For that reason, if you want to react on different file types _during_ the drag operation, _you have to use_ mime types and not extensions! For example, the following example won't work even in Chrome:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Accept(props) {
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject
} = useDropzone({
accept: {
'image/jpeg': ['.jpeg', '.png']
}
});
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
{isDragAccept && (<p>All files will be accepted</p>)}
{isDragReject && (<p>Some files will be rejected</p>)}
{!isDragActive && (<p>Drop some files here ...</p>)}
</div>
</div>
);
}
<Accept />
```
but this one will:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Accept(props) {
const {
getRootProps,
getInputProps,
isDragActive,
isDragAccept,
isDragReject
} = useDropzone({
accept: {
'image/*': ['.jpeg', '.png']
}
});
return (
<div className="container">
<div {...getRootProps({className: "dropzone"})}>
<input {...getInputProps()} />
{isDragAccept && (<p>All files will be accepted</p>)}
{isDragReject && (<p>Some files will be rejected</p>)}
{!isDragActive && (<p>Drop some files here ...</p>)}
</div>
</div>
);
}
<Accept />
```
### Notes
Mime type determination is not reliable across platforms. CSV files, for example, are reported as text/plain under macOS but as application/vnd.ms-excel under Windows. In some cases there might not be a mime type set at all.

View File

@@ -0,0 +1,70 @@
The `useDropzone` hook just binds the necessary handlers to create a drag 'n' drop zone.
Use the `getRootProps()` fn to get the props required for drag 'n' drop and use them on any element.
For click and keydown behavior, use the `getInputProps()` fn and use the returned props on an `<input>`.
Furthermore, the hook supports folder drag 'n' drop by default. See [file-selector](https://github.com/react-dropzone/file-selector) for more info about supported browsers.
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Basic(props) {
const {acceptedFiles, getRootProps, getInputProps} = useDropzone();
const files = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<Basic />
```
Dropzone with `disabled` property:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Basic(props) {
const {acceptedFiles, getRootProps, getInputProps} = useDropzone({
disabled: true
});
const files = acceptedFiles.map(file => (
<li key={file.name}>
{file.name} - {file.size} bytes
</li>
));
return (
<section className="container">
<div {...getRootProps({className: 'dropzone disabled'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<Basic />
```

View File

@@ -0,0 +1,45 @@
If you're still using class components, you can use the [`<Dropzone>`](https://react-dropzone.js.org/#components) component provided by the lib:
```jsx harmony
import React, {Component} from 'react';
import Dropzone from 'react-dropzone';
class Basic extends Component {
constructor() {
super();
this.onDrop = (files) => {
this.setState({files})
};
this.state = {
files: []
};
}
render() {
const files = this.state.files.map(file => (
<li key={file.name}>
{file.name} - {file.size} bytes
</li>
));
return (
<Dropzone onDrop={this.onDrop}>
{({getRootProps, getInputProps}) => (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
)}
</Dropzone>
);
}
}
<Basic />
```

View File

@@ -0,0 +1,164 @@
If you'd like to prevent drag events propagation from the child to parent, you can use the `{noDragEventsBubbling}` property on the child:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function OuterDropzone(props) {
const {getRootProps} = useDropzone({
// Note how this callback is never invoked if drop occurs on the inner dropzone
onDrop: files => console.log(files)
});
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
<InnerDropzone />
<p>Outer dropzone</p>
</div>
</div>
);
}
function InnerDropzone(props) {
const {getRootProps} = useDropzone({noDragEventsBubbling: true});
return (
<div {...getRootProps({className: 'dropzone'})}>
<p>Inner dropzone</p>
</div>
);
}
<OuterDropzone />
```
Note that internally we use `event.stopPropagation()` to achieve the behavior illustrated above, but this comes with its own [drawbacks](https://javascript.info/bubbling-and-capturing#stopping-bubbling).
If you'd like to selectively turn off the default dropzone behavior for `onClick`, use the `{noClick}` property:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function DropzoneWithoutClick(props) {
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noClick: true});
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Dropzone without click events</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<DropzoneWithoutClick />
```
If you'd like to selectively turn off the default dropzone behavior for `onKeyDown`, `onFocus` and `onBlur`, use the `{noKeyboard}` property:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function DropzoneWithoutKeyboard(props) {
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noKeyboard: true});
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Dropzone without keyboard events</p>
<em>(SPACE/ENTER and focus events are disabled)</em>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<DropzoneWithoutKeyboard />
```
Or you can prevent the default behavior for both click and keyboard events if you omit the input:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function DropzoneWithoutClick(props) {
const {getRootProps, acceptedFiles} = useDropzone();
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<p>Dropzone without click events</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<DropzoneWithoutClick />
```
**NOTE** If the browser supports the [File System Access API](https://developer.mozilla.org/en-US/docs/Web/API/File_System_Access_API) and you've set the `useFsAccessApi` to true, removing the `<input>` has no effect.
If you'd like to selectively turn off the default dropzone behavior for drag events, use the `{noDrag}` property:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function DropzoneWithoutDrag(props) {
const {getRootProps, getInputProps, acceptedFiles} = useDropzone({noDrag: true});
const files = acceptedFiles.map(file => <li key={file.path}>{file.path}</li>);
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Dropzone with no drag events</p>
<em>(Drag 'n' drop is disabled)</em>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
<DropzoneWithoutDrag />
```
Keep in mind that if you provide your own callback handlers as well and use `event.stopPropagation()`, it will prevent the default dropzone behavior:
```jsx harmony
import React from 'react';
import Dropzone from 'react-dropzone';
// Note that there will be nothing logged when files are dropped
<Dropzone onDrop={files => console.log(files)}>
{({getRootProps, getInputProps}) => (
<div className="container">
<div
{...getRootProps({
className: 'dropzone',
onDrop: event => event.stopPropagation()
})}
>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
</div>
)}
</Dropzone>
```

View File

@@ -0,0 +1,90 @@
You can programmatically invoke the default OS file prompt; just use the `open` method returned by the hook.
**Note** that for security reasons most browsers require popups and dialogues to originate from a direct user interaction (i.e. click).
If you are calling `open()` asynchronously, theres a good chance its going to be blocked by the browser. So if you are calling `open()` asynchronously, be sure there is no more than *1000ms* delay between user interaction and `open()` call.
Due to the lack of official docs on this (at least we havent found any. If you know one, feel free to open PR), there is no guarantee that **allowed delay duration** will not be changed in later browser versions. Since implementations may differ between different browsers, avoid calling open asynchronously if possible.
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Dropzone(props) {
const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({
// Disable click and keydown behavior
noClick: true,
noKeyboard: true
});
const files = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here</p>
<button type="button" onClick={open}>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
);
}
<Dropzone />
```
Or use the `ref` exposed by the `<Dropzone>` component:
```jsx harmony
import React, {createRef} from 'react';
import Dropzone from 'react-dropzone';
const dropzoneRef = createRef();
const openDialog = () => {
// Note that the ref is set async,
// so it might be null at some point
if (dropzoneRef.current) {
dropzoneRef.current.open()
}
};
// Disable click and keydown behavior on the <Dropzone>
<Dropzone ref={dropzoneRef} noClick noKeyboard>
{({getRootProps, getInputProps, acceptedFiles}) => {
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here</p>
<button
type="button"
onClick={openDialog}
>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>
{acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
))}
</ul>
</aside>
</div>
);
}}
</Dropzone>
```

View File

@@ -0,0 +1,69 @@
React-dropzone does not submit the files in form submissions by default.
If you need this behavior, you can add a hidden file input, and set the files into it.
```jsx harmony
import React, {useRef} from 'react';
import {useDropzone} from 'react-dropzone';
function Dropzone(props) {
const {required, name} = props;
const hiddenInputRef = useRef(null);
const {getRootProps, getInputProps, open, acceptedFiles} = useDropzone({
onDrop: (incomingFiles) => {
if (hiddenInputRef.current) {
// Note the specific way we need to munge the file into the hidden input
// https://stackoverflow.com/a/68182158/1068446
const dataTransfer = new DataTransfer();
incomingFiles.forEach((v) => {
dataTransfer.items.add(v);
});
hiddenInputRef.current.files = dataTransfer.files;
}
}
});
const files = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
return (
<div className="container">
<div {...getRootProps({className: 'dropzone'})}>
{/*
Add a hidden file input
Best to use opacity 0, so that the required validation message will appear on form submission
*/}
<input type ="file" name={name} required={required} style ={{opacity: 0}} ref={hiddenInputRef}/>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here</p>
<button type="button" onClick={open}>
Open File Dialog
</button>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</div>
);
}
<form onSubmit={(e) => {
e.preventDefault();
// Now get the form data as you regularly would
const formData = new FormData(e.currentTarget);
const file = formData.get("my-file");
alert(file.name);
}}>
<Dropzone name ="my-file" required/>
<button type="submit">Submit</button>
</form>
```

View File

@@ -0,0 +1,58 @@
By providing `maxFiles` prop you can limit how many files the dropzone accepts.
**Note** that this prop is enabled when the `multiple` prop is enabled.
The default value for this prop is 0, which means there's no limitation to how many files are accepted.
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function AcceptMaxFiles(props) {
const {
acceptedFiles,
fileRejections,
getRootProps,
getInputProps
} = useDropzone({
maxFiles:2
});
const acceptedFileItems = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
const fileRejectionItems = fileRejections.map(({ file, errors }) => {
return (
<li key={file.path}>
{file.path} - {file.size} bytes
<ul>
{errors.map(e => <li key={e.code}>{e.message}</li>)}
</ul>
</li>
)
});
return (
<section className="container">
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
<em>(2 files are the maximum number of files you can drop here)</em>
</div>
<aside>
<h4>Accepted files</h4>
<ul>{acceptedFileItems}</ul>
<h4>Rejected files</h4>
<ul>{fileRejectionItems}</ul>
</aside>
</section>
);
}
<AcceptMaxFiles />
```

View File

@@ -0,0 +1,32 @@
If you'd like to use [react without JSX](https://reactjs.org/docs/react-without-jsx.html) you can:
```js harmony
import React, {useCallback, useState} from 'react';
import {useDropzone} from 'react-dropzone';
const e = React.createElement
function Basic () {
const [files, setFiles] = useState([]);
const onDrop = useCallback(files => setFiles(files), [setFiles]);
const {getRootProps, getInputProps} = useDropzone({onDrop});
const fileList = files.map(
file => React.createElement('li', {key: file.name}, `${file.name} - ${file.size} bytes`)
);
return e('section', {className: 'container'}, [
e('div', getRootProps({className: 'dropzone', key: 'dropzone'}), [
e('input', getInputProps({key: 'input'})),
e('p', {key: 'desc'}, "Drag 'n' drop some files here, or click to select files")
]),
e('aside', {key: 'filesContainer'}, [
e('h4', {key: 'title'}, 'Files'),
e('ul', {key: 'fileList'}, fileList)
])
]);
}
Basic()
```

View File

@@ -0,0 +1,146 @@
If you'd like to integrate the dropzone with the [Pintura](https://pqina.nl/pintura/?ref=react-dropzone) image editor, you just need to pass either of the selected images to the `openDefaultEditor()` method exported by Pintura:
```jsx static
import React, { useState, useEffect } from 'react';
// React Dropzone
import { useDropzone } from 'react-dropzone';
// Pintura Image Editor
import 'pintura/pintura.css';
import { openDefaultEditor } from 'pintura';
// Based on the default React Dropzone image thumbnail example
// The `thumbButton` style positions the edit button in the bottom right corner of the thumbnail
const thumbsContainer = {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
marginTop: 16,
padding: 20,
};
const thumb = {
position: 'relative',
display: 'inline-flex',
borderRadius: 2,
border: '1px solid #eaeaea',
marginBottom: 8,
marginRight: 8,
width: 100,
height: 100,
padding: 4,
boxSizing: 'border-box',
};
const thumbInner = {
display: 'flex',
minWidth: 0,
overflow: 'hidden',
};
const img = {
display: 'block',
width: 'auto',
height: '100%',
};
const thumbButton = {
position: 'absolute',
right: 10,
bottom: 10,
};
// This function is called when the user taps the edit button.
// It opens the editor and returns the modified file when done
const editImage = (image, done) => {
const imageFile = image.pintura ? image.pintura.file : image;
const imageState = image.pintura ? image.pintura.data : {};
const editor = openDefaultEditor({
src: imageFile,
imageState,
});
editor.on('close', () => {
// the user cancelled editing the image
});
editor.on('process', ({ dest, imageState }) => {
Object.assign(dest, {
pintura: { file: imageFile, data: imageState },
});
done(dest);
});
};
function App() {
const [files, setFiles] = useState([]);
const { getRootProps, getInputProps } = useDropzone({
accept: {
'image/*': [],
},
onDrop: (acceptedFiles) => {
setFiles(
acceptedFiles.map((file) =>
Object.assign(file, {
preview: URL.createObjectURL(file),
})
)
);
},
});
const thumbs = files.map((file, index) => (
<div style={thumb} key={file.name}>
<div style={thumbInner}>
<img src={file.preview} style={img} alt="" />
</div>
<button
style={thumbButton}
onClick={() =>
editImage(file, (output) => {
const updatedFiles = [...files];
// replace original image with new image
updatedFiles[index] = output;
// revoke preview URL for old image
if (file.preview) URL.revokeObjectURL(file.preview);
// set new preview URL
Object.assign(output, {
preview: URL.createObjectURL(output),
});
// update view
setFiles(updatedFiles);
})
}
>
Edit
</button>
</div>
));
useEffect(
() => () => {
// Make sure to revoke the Object URL to avoid memory leaks
files.forEach((file) => URL.revokeObjectURL(file.preview));
},
[files]
);
return (
<section className="container">
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside style={thumbsContainer}>{thumbs}</aside>
</section>
);
}
export default App;
```

View File

@@ -0,0 +1,55 @@
The hook accepts a `getFilesFromEvent` prop that enhances the handling of dropped file system objects and allows more flexible use of them e.g. passing a function that accepts drop event of a folder and resolves it to an array of files adds plug-in functionality of folders drag-and-drop.
Though, note that the provided `getFilesFromEvent` fn must return a `Promise` with a list of `File` objects (or `DataTransferItem` of `{kind: 'file'}` when data cannot be accessed).
Otherwise, the results will be discarded and none of the drag events callbacks will be invoked.
In case you need to extend the `File` with some additional properties, you should use [Object.defineProperty()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) so that the result will still pass through our filter:
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
function Plugin(props) {
const {acceptedFiles, getRootProps, getInputProps} = useDropzone({
getFilesFromEvent: event => myCustomFileGetter(event)
});
const files = acceptedFiles.map(f => (
<li key={f.name}>
{f.name} has <strong>myProps</strong>: {f.myProp === true ? 'YES' : ''}
</li>
));
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside>
<h4>Files</h4>
<ul>{files}</ul>
</aside>
</section>
);
}
async function myCustomFileGetter(event) {
const files = [];
const fileList = event.dataTransfer ? event.dataTransfer.files : event.target.files;
for (var i = 0; i < fileList.length; i++) {
const file = fileList.item(i);
Object.defineProperty(file, 'myProp', {
value: true
});
files.push(file);
}
return files;
}
<Plugin />
```

View File

@@ -0,0 +1,86 @@
Starting with version 7.0.0, the `{preview}` property generation on the [File](https://developer.mozilla.org/en-US/docs/Web/API/File) objects and the `{disablePreview}` property on the `<Dropzone>` have been removed.
If you need the `{preview}`, it can be easily achieved in the `onDrop()` callback:
```jsx harmony
import React, {useEffect, useState} from 'react';
import {useDropzone} from 'react-dropzone';
const thumbsContainer = {
display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap',
marginTop: 16
};
const thumb = {
display: 'inline-flex',
borderRadius: 2,
border: '1px solid #eaeaea',
marginBottom: 8,
marginRight: 8,
width: 100,
height: 100,
padding: 4,
boxSizing: 'border-box'
};
const thumbInner = {
display: 'flex',
minWidth: 0,
overflow: 'hidden'
};
const img = {
display: 'block',
width: 'auto',
height: '100%'
};
function Previews(props) {
const [files, setFiles] = useState([]);
const {getRootProps, getInputProps} = useDropzone({
accept: {
'image/*': []
},
onDrop: acceptedFiles => {
setFiles(acceptedFiles.map(file => Object.assign(file, {
preview: URL.createObjectURL(file)
})));
}
});
const thumbs = files.map(file => (
<div style={thumb} key={file.name}>
<div style={thumbInner}>
<img
src={file.preview}
style={img}
// Revoke data uri after image is loaded
onLoad={() => { URL.revokeObjectURL(file.preview) }}
/>
</div>
</div>
));
useEffect(() => {
// Make sure to revoke the data uris to avoid memory leaks, will run on unmount
return () => files.forEach(file => URL.revokeObjectURL(file.preview));
}, [files]);
return (
<section className="container">
<div {...getRootProps({className: 'dropzone'})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
<aside style={thumbsContainer}>
{thumbs}
</aside>
</section>
);
}
<Previews />
```

View File

@@ -0,0 +1,126 @@
The hook fn doesn't set any styles on either of the prop fns (`getRootProps()`/`getInputProps()`).
### Using inline styles
```jsx harmony
import React, {useMemo} from 'react';
import {useDropzone} from 'react-dropzone';
const baseStyle = {
flex: 1,
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
padding: '20px',
borderWidth: 2,
borderRadius: 2,
borderColor: '#eeeeee',
borderStyle: 'dashed',
backgroundColor: '#fafafa',
color: '#bdbdbd',
outline: 'none',
transition: 'border .24s ease-in-out'
};
const focusedStyle = {
borderColor: '#2196f3'
};
const acceptStyle = {
borderColor: '#00e676'
};
const rejectStyle = {
borderColor: '#ff1744'
};
function StyledDropzone(props) {
const {
getRootProps,
getInputProps,
isFocused,
isDragAccept,
isDragReject
} = useDropzone({accept: {'image/*': []}});
const style = useMemo(() => ({
...baseStyle,
...(isFocused ? focusedStyle : {}),
...(isDragAccept ? acceptStyle : {}),
...(isDragReject ? rejectStyle : {})
}), [
isFocused,
isDragAccept,
isDragReject
]);
return (
<div className="container">
<div {...getRootProps({style})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</div>
</div>
);
}
<StyledDropzone />
```
### Using styled-components
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
import styled from 'styled-components';
const getColor = (props) => {
if (props.isDragAccept) {
return '#00e676';
}
if (props.isDragReject) {
return '#ff1744';
}
if (props.isFocused) {
return '#2196f3';
}
return '#eeeeee';
}
const Container = styled.div`
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border-width: 2px;
border-radius: 2px;
border-color: ${props => getColor(props)};
border-style: dashed;
background-color: #fafafa;
color: #bdbdbd;
outline: none;
transition: border .24s ease-in-out;
`;
function StyledDropzone(props) {
const {
getRootProps,
getInputProps,
isFocused,
isDragAccept,
isDragReject
} = useDropzone({accept: {'image/*': []}});
return (
<div className="container">
<Container {...getRootProps({isFocused, isDragAccept, isDragReject})}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
</Container>
</div>
);
}
<StyledDropzone />
```

View File

@@ -0,0 +1,37 @@
.container {
display: flex;
flex-direction: column;
font-family: sans-serif;
}
.container > p {
font-size: 1rem;
}
.container > em {
font-size: .8rem;
}
.dropzone {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
border-width: 2px;
border-radius: 2px;
border-color: #eeeeee;
border-style: dashed;
background-color: #fafafa;
color: #bdbdbd;
outline: none;
transition: border .24s ease-in-out;
}
.dropzone:focus {
border-color: #2196f3;
}
.dropzone.disabled {
opacity: 0.6;
}

View File

@@ -0,0 +1,68 @@
By providing `validator` prop you can specify custom validation for files.
The value must be a function that accepts File object and returns null if file should be accepted or error object/array of error objects if file should be rejected.
```jsx harmony
import React from 'react';
import {useDropzone} from 'react-dropzone';
const maxLength = 20;
function nameLengthValidator(file) {
if (file.name.length > maxLength) {
return {
code: "name-too-large",
message: `Name is larger than ${maxLength} characters`
};
}
return null
}
function CustomValidation(props) {
const {
acceptedFiles,
fileRejections,
getRootProps,
getInputProps
} = useDropzone({
validator: nameLengthValidator
});
const acceptedFileItems = acceptedFiles.map(file => (
<li key={file.path}>
{file.path} - {file.size} bytes
</li>
));
const fileRejectionItems = fileRejections.map(({ file, errors }) => (
<li key={file.path}>
{file.path} - {file.size} bytes
<ul>
{errors.map(e => (
<li key={e.code}>{e.message}</li>
))}
</ul>
</li>
));
return (
<section className="container">
<div {...getRootProps({ className: 'dropzone' })}>
<input {...getInputProps()} />
<p>Drag 'n' drop some files here, or click to select files</p>
<em>(Only files with name less than 20 characters will be accepted)</em>
</div>
<aside>
<h4>Accepted files</h4>
<ul>{acceptedFileItems}</ul>
<h4>Rejected files</h4>
<ul>{fileRejectionItems}</ul>
</aside>
</section>
);
}
<CustomValidation />
```