react-files
Advanced tools
Comparing version 0.0.2 to 1.0.0
{ | ||
"name": "react-files", | ||
"version": "0.0.2", | ||
"version": "1.0.0", | ||
"main": "src/Files.js", | ||
@@ -5,0 +5,0 @@ "description": "A file input (dropzone) management component for React", |
react-files | ||
======================= | ||
A file input (dropzone) management component for React. | ||
> A file input (dropzone) management component for React | ||
## Demo | ||
![Alt text](/demo.gif?raw=true "Demo") | ||
## Installation | ||
@@ -17,12 +21,77 @@ | ||
``` | ||
import React from 'react' | ||
import ReactDOM from 'react-dom' | ||
import Files from './Files' | ||
var FilesDemo = React.createClass({ | ||
onSubmit: function(files) { | ||
console.log(files) | ||
}, | ||
ReactDOM.render(<Files name="Jane" />, document.getElementById('container')); | ||
onUnaccepted: function(file) { | ||
console.log(file.name + ' is not a valid file type.') | ||
}, | ||
render: function() { | ||
return ( | ||
<div className="files"> | ||
<Files | ||
onSubmit={this.onSubmit} | ||
onUnaccepted={this.onUnaccepted} | ||
accepts={['image/*', 'application/pdf', '.txt']} | ||
/> | ||
</div> | ||
) | ||
} | ||
}) | ||
ReactDOM.render(<FilesDemo />, document.getElementById('container')) | ||
``` | ||
### Test | ||
### Props | ||
> `onSubmit(files)` - Function | ||
Perform work on files added when submit is clicked. | ||
> `onUnaccepted(file)` - Function | ||
Perform work or notify the user when a file is dropped/added that is unacceptable. | ||
> `accepts` - Array of String | ||
Control what types of generic/specific MIME types, or specific extensions can be dropped/added. | ||
Example | ||
```js | ||
accepts={['image/*', 'video/mp4', 'audio/*', '.pdf', '.txt']} | ||
``` | ||
### Styling | ||
Be sure to style your Files component, available selectors are (view `style.css`): | ||
- .files-container | ||
- .files-dropzone-outer | ||
- .files-dropzone | ||
- .files-dropzone:before | ||
- .files-dropzone-ondragenter | ||
- .files-buttons | ||
- .files-button-submit | ||
- .files-button-submit:before | ||
- .files-button-clear | ||
- .files-button-clear:before | ||
- .files-list | ||
- .files-list ul | ||
- .files-list li:last-child | ||
- .files-list-item | ||
- .files-list-item-content | ||
- .files-list-item-content-item | ||
- .files-list-item-content-item-1 | ||
- .files-list-item-content-item-2 | ||
- .files-list-item-preview | ||
- .files-list-item-preview-image | ||
- .files-list-item-preview-extension | ||
- .files-list-item-remove | ||
- .files-list-item-remove-image | ||
### Test (todo) | ||
``` | ||
npm test | ||
@@ -29,0 +98,0 @@ ``` |
179
src/Files.js
@@ -6,19 +6,126 @@ import React from 'react' | ||
super(props, context) | ||
this.onClick = this.onClick.bind(this) | ||
this.onDrop = this.onDrop.bind(this) | ||
// this.onDragStart = this.onDragStart.bind(this) | ||
// this.onDragEnter = this.onDragEnter.bind(this) | ||
// this.onDragLeave = this.onDragLeave.bind(this) | ||
// this.onDragOver = this.onDragOver.bind(this) | ||
this.onSubmit = this.onSubmit.bind(this) | ||
this.onUnaccepted = this.onUnaccepted.bind(this) | ||
this.onClear = this.onClear.bind(this) | ||
this.openFileChooser = this.openFileChooser.bind(this) | ||
this.removeFile = this.removeFile.bind(this) | ||
this.fileAcceptable = this.fileAcceptable.bind(this) | ||
this.id = 1 | ||
this.state = { | ||
files: [] | ||
} | ||
} | ||
onClick() { | ||
onDrop(event) { | ||
event.preventDefault() | ||
this.onDragLeave(event) | ||
// Collect added files and cast pseudo-array to Array, then return to method | ||
const filesAdded = event.dataTransfer ? event.dataTransfer.files : event.target.files | ||
let files = [] | ||
for (let i = 0; i < filesAdded.length; i++) { | ||
let file = filesAdded[i] | ||
file.id = 'files-list-item-' + this.id++ | ||
if (file.type && this.mimeTypeLeft(file.type) === 'image') { | ||
file.preview = { | ||
type: 'image', | ||
url: window.URL.createObjectURL(file) | ||
} | ||
} else { | ||
file.preview = { | ||
type: 'file', | ||
extension: this.fileExtension(file) | ||
} | ||
} | ||
if (this.fileAcceptable(file)) files.unshift(file) | ||
} | ||
this.setState({ files: [...files, ...this.state.files] }) | ||
} | ||
onDragOver(event) { | ||
event.preventDefault() | ||
event.stopPropagation() | ||
} | ||
onDragEnter(event) { | ||
event.target.className += ' files-dropzone-ondragenter' | ||
} | ||
onDragLeave(event) { | ||
event.target.className = event.target.className.replace(' files-dropzone-ondragenter', '') | ||
} | ||
openFileChooser() { | ||
this.inputElement.value = null | ||
this.inputElement.click() | ||
} | ||
onDrop(e) { | ||
const files = e.dataTransfer ? e.dataTransfer.files : e.target.files | ||
this.props.onDrop(files) | ||
removeFile(fileId) { | ||
this.setState({ | ||
files: this.state.files.filter(file => file.id !== fileId) | ||
}) | ||
} | ||
fileAcceptable(file) { | ||
let accepts = this.props.accepts | ||
if (accepts) { | ||
if (accepts.indexOf(this.fileExtension(file)) !== -1) return true | ||
if (file.type) { | ||
let typeLeft = this.mimeTypeLeft(file.type) | ||
let typeRight = this.mimeTypeRight(file.type) | ||
for (let i = 0; i < accepts.length; i++) { | ||
let accept = accepts[i] | ||
let acceptLeft = accept.split('/')[0] | ||
let acceptRight = accept.split('/')[1] | ||
if (acceptLeft && acceptRight) { | ||
if (acceptLeft === typeLeft && acceptRight === '*') { | ||
return true | ||
} | ||
if (acceptLeft === typeLeft && acceptRight === typeRight) { | ||
return true | ||
} | ||
} | ||
} | ||
} | ||
this.onUnaccepted(file) | ||
return false | ||
} else { | ||
return true | ||
} | ||
} | ||
mimeTypeLeft(mime) { | ||
return mime.split('/')[0] | ||
} | ||
mimeTypeRight(mime) { | ||
return mime.split('/')[1] | ||
} | ||
fileExtension(file) { | ||
let extensionSplit = file.name.split('.') | ||
if (extensionSplit.length > 1) { | ||
return '.' + extensionSplit[extensionSplit.length - 1] | ||
} else { | ||
return 'none' | ||
} | ||
} | ||
onSubmit() { | ||
this.props.onSubmit.call(this, this.state.files); | ||
} | ||
onUnaccepted(file) { | ||
this.props.onUnaccepted.call(this, file); | ||
} | ||
onClear() { | ||
this.setState({ | ||
files: [] | ||
}) | ||
} | ||
render() { | ||
@@ -36,10 +143,50 @@ | ||
<div | ||
className="div" | ||
onClick={this.onClick} | ||
onDrop={this.onDrop} | ||
className="files-container" | ||
> | ||
<input | ||
// {...inputProps/* expand user provided inputProps first so inputAttributes override them */} | ||
{...inputAttributes} | ||
/> | ||
<input | ||
// {...inputProps/* expand user provided inputProps first so inputAttributes override them */} | ||
{...inputAttributes} | ||
/> | ||
<div | ||
className="files-dropzone-outer" | ||
> | ||
<div className="files-dropzone" | ||
onClick={this.openFileChooser} | ||
onDrop={this.onDrop} | ||
onDragOver={this.onDragOver} | ||
onDragEnter={this.onDragEnter} | ||
onDragLeave={this.onDragLeave} | ||
/> | ||
</div> | ||
{this.props.children} | ||
{ | ||
this.state.files.length > 0 | ||
? <div> | ||
<div className="files-list"> | ||
<ul>{this.state.files.map((file) => | ||
<li className="files-list-item" key={file.id}> | ||
<div className="files-list-item-preview"> | ||
{file.preview.type === 'image' | ||
? <img className="files-list-item-preview-image" src={file.preview.url} /> | ||
: <div className="files-list-item-preview-extension">{file.preview.extension}</div>} | ||
</div> | ||
<div className="files-list-item-content"> | ||
<div id="files-list-item-content-item-1" className="files-list-item-content-item">{file.name}</div> | ||
<div id="files-list-item-content-item-2" className="files-list-item-content-item">{file.size} bytes</div> | ||
</div> | ||
<div | ||
id={file.id} | ||
className="files-list-item-remove" | ||
onClick={this.removeFile.bind(this, file.id)} | ||
/> | ||
</li> | ||
)}</ul> | ||
</div> | ||
<div className="files-buttons"> | ||
<div onClick={this.onSubmit} className="files-button-submit" /> | ||
<div onClick={this.onClear} className="files-button-clear" /> | ||
</div> | ||
</div> | ||
: null | ||
} | ||
</div> | ||
@@ -46,0 +193,0 @@ |
@@ -6,16 +6,17 @@ import React from 'react' | ||
var FilesDemo = React.createClass({ | ||
onDrop: function (files) { | ||
onSubmit: function(files) { | ||
console.log(files) | ||
}, | ||
onClick: function () { | ||
console.log('click') | ||
onUnaccepted: function(file) { | ||
console.log(file.name + ' is not a valid file type.') | ||
}, | ||
render: function () { | ||
render: function() { | ||
return ( | ||
<div> | ||
<Files className="wow" onDrop={this.onDrop} onClick={this.onClick}> | ||
<div>Try dropping some files here, or click to select files.</div> | ||
</Files> | ||
<div className="files"> | ||
<Files | ||
onSubmit={this.onSubmit} | ||
onUnaccepted={this.onUnaccepted} | ||
/> | ||
</div> | ||
@@ -26,2 +27,2 @@ ) | ||
ReactDOM.render(<FilesDemo name="Jane" />, document.getElementById('container')) | ||
ReactDOM.render(<FilesDemo />, document.getElementById('container')) |
@@ -9,2 +9,3 @@ import React from 'react' | ||
/* | ||
describe('Files', () => { | ||
@@ -19,1 +20,2 @@ it('works', () => { | ||
}) | ||
*/ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
559764
14
386
0
102