Reputation: 41
I'm making my own dropzone and I want to do an action while specific file extension dragging, I found that onDragEnter
can't access file types, onDrop
only can make that.
But I found a library that makes what I want to do, I tried hard to know from the source code how they do it but I fail. here is the link of the code
https://react-dropzone.netlify.com/#!/Accepting%20specific%20file%20types/5
Upvotes: 4
Views: 2311
Reputation: 12796
Well, you are right that the onDragEnter
doesn't give you this information, however you can get the information from the onDragOver
.
To do this with React, I guess the easiest is to use the ref callback to get the element where you want to drop to (or use the events on the element directly).
setTargetDropZone( element ) {
if (this.dropZone) {
// remove any previous attached event
this.dropZone.removeEventListener('dragover', this.onDragOver );
}
// update the local field with the new element
this.dropZone = element;
if (!this.dropZone) {
return;
}
// if it is not null attach the new event
this.dropZone.addEventListener( 'dragover', this.onDragOver );
}
Somewhere you would have to define which file types you want to accept, and then you have to handle the incoming data to filter it.
const { fileTypes = null } = this.props;
const { items } = e.dataTransfer;
let dropCount = items.length;
// if it is restricted, count how many actually match
if (fileTypes) {
// items is not an array, so you could use Array.from here
dropCount = Array.from( items ).filter( item => fileTypes.some( type => item.type.includes( type ) ) ).length;
}
So if you combine this code, in the end, you could end up with something like this
const { Component } = React;
const DropState = {
'All': '0',
'No': '1',
'Some': '2',
'0': 'All',
'1': 'No',
'2': 'Some'
};
class DropZone extends Component {
constructor() {
super();
this.state = {
dropState: DropState.All,
dropCount: 0
};
this.setTargetDropZone = this.setTargetDropZone.bind( this );
}
onDragOver = e => {
const { fileTypes = null } = this.props;
const { items } = e.dataTransfer;
let dropCount = items.length;
if (fileTypes) {
dropCount = Array.from( items ).filter( item => fileTypes.some( type => item.type.includes( type ) ) ).length;
}
this.setState( {
dropCount,
dropState: dropCount === items.length ? DropState.All : dropCount === 0 ? DropState.No : DropState.Some
} );
}
setTargetDropZone( element ) {
if (this.dropZone) {
this.dropZone.removeEventListener('dragover', this.onDragOver );
}
this.dropZone = element;
if (!this.dropZone) {
return;
}
this.dropZone.addEventListener( 'dragover', this.onDragOver );
}
render() {
const { dropState, dropCount } = this.state;
const { placeholder = 'Drop some files here' } = this.props;
return (
<div ref={this.setTargetDropZone} style={{border: 'solid #a0a0a0 1px' }}>
{ dropCount > 0 && <span>{ `${DropState[dropState]} will be accepted (${dropCount})` }</span> }
{ dropCount === 0 && placeholder }
</div>
);
}
}
const container = document.querySelector('#container');
ReactDOM.render( <DropZone fileTypes={ ['image/jpg', 'image/jpeg', 'image/png' ] } />, container );
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="container">
</div>
Upvotes: 0
Reputation: 97672
Actually the file type is available, I don't know how react dropzone does it, but you can access the type via DragEvent.dataTransfer.items[0].type
, items
is the array of items being dragged so you can access the others via items[1]
etc.
$('div').on('dragover',function(e){
console.log(e.originalEvent.dataTransfer.items[0].type);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>dropzone</div>
Upvotes: 2