Skip to content
Snippets Groups Projects
Commit 476ca1bf authored by Alf Eaton's avatar Alf Eaton
Browse files

Add atoms and molecules for supplementary materials

parent aeb8aa5e
No related branches found
No related tags found
No related merge requests found
...@@ -21,8 +21,8 @@ ...@@ -21,8 +21,8 @@
}, },
"devDependencies": { "devDependencies": {
"babel-core": "^6.26.0", "babel-core": "^6.26.0",
"babel-loader": "^7.1.2",
"babel-jest": "^20.0.3", "babel-jest": "^20.0.3",
"babel-loader": "^7.1.2",
"babel-preset-env": "^1.6.0", "babel-preset-env": "^1.6.0",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"babel-preset-stage-2": "^6.24.1", "babel-preset-stage-2": "^6.24.1",
......
import React from 'react'
import classes from './File.local.css'
const extension = ({ name }) => name.replace(/^.+\./, '')
const File = ({ value, file, error, progress }) => (
<div className={classes.root}>
<div className={classes.icon}>
<div className={classes.extension}>
{extension(value)}
</div>
</div>
<div className={classes.name}>{value.name}</div>
</div>
)
export default File
.root {
display: inline-flex;
flex-direction: column;
align-items: center;
}
.icon {
width: 70px;
height: 100px;
background: #ddd;
position: relative;
margin: 5px;
}
.extension {
position: absolute;
top: 20px;
right: 0;
left: 20px;
text-align: center;
text-transform: uppercase;
background: #888;
color: white;
font-size: 12px;
padding: 2px;
}
.name {
font-style: italic;
font-size: 90%;
color: gray;
margin: 5px;
}
A file.
```js
const value = {
name: faker.system.commonFileName(),
// type: faker.system.commonFileType(),
// size: faker.random.number(),
};
<File value={value}/>
```
Upload progress is displayed as an overlay.
```js
const value = {
name: faker.system.commonFileName(),
};
<File value={value} progress={0.5}/>
```
An upload error is displayed above the file.
```js
const value = {
name: faker.system.commonFileName(),
};
<File value={value} error="There was an error"/>
```
import React from 'react'
import classes from './UploadingFile.local.css'
// TODO: cancel button
const extension = ({ name }) => name.replace(/^.+\./, '')
const UploadingFile = ({ file, error, progress }) => (
<div className={classes.root}>
{!!error && (
<div className={classes.error}>
{error}
</div>
)}
<div className={classes.icon}>
{!!progress && (
<div
className={classes.progress}
style={{ top: (progress * 100) + '%' }}/>
)}
<div className={classes.extension}>
{extension(file)}
</div>
</div>
<div className={classes.name}>{file.name}</div>
</div>
)
export default UploadingFile
.root {
display: inline-flex;
flex-direction: column;
align-items: center;
opacity: 0.5;
}
.error {
color: red;
}
.icon {
width: 70px;
height: 100px;
background: #ddd;
position: relative;
margin: 5px;
}
.progress {
opacity: 0.5;
background: lawngreen;
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
.extension {
position: absolute;
top: 20px;
right: 0;
left: 20px;
text-align: center;
text-transform: uppercase;
background: #888;
color: white;
font-size: 12px;
padding: 2px;
}
.name {
font-style: italic;
font-size: 90%;
color: gray;
margin: 5px;
}
A file that's being uploaded.
```js
const file = {
name: faker.system.commonFileName()
};
<UploadingFile file={file}/>
```
Upload progress is displayed as an overlay.
```js
const file = {
name: faker.system.commonFileName(),
};
<UploadingFile file={file} progress={0.5}/>
```
An upload error is displayed above the file.
```js
const file = {
name: faker.system.commonFileName(),
};
<UploadingFile file={file} error="There was an error"/>
```
import React from 'react'
import classes from './Files.local.css'
import Upload from './Upload'
import File from '../atoms/File'
class Files extends React.Component {
constructor (props) {
super(props)
this.state = {
values: props.value || [],
uploads: []
}
}
handleClick = () => {
this.fileInput.click()
}
handleChange = event => {
const { uploadFile } = this.props
const { uploads } = this.state
Array.from(event.target.files).forEach(file => {
uploads.push({
file,
request: uploadFile(file)
})
})
this.setState({ uploads })
}
render () {
const { name } = this.props
const { values, uploads } = this.state
return (
<div className={classes.root}>
<div className={classes.upload}>
<button
type="button"
className={classes.button}
onClick={() => this.fileInput.click()}>
Upload files
</button>
<input
className={classes.input}
ref={input => (this.fileInput = input)}
type="file"
name={name}
multiple
onChange={this.handleChange}/>
</div>
<div className={classes.files}>
{uploads && uploads.map(upload => (
<Upload
file={upload.file}
request={upload.request}/>
))}
{values && values.map(value => (
<File
key={value.id}
value={value}/>
))}
</div>
</div>
)
}
}
export default Files
.input {
display: none;
}
.button {
border: none;
background: transparent;
padding: 10px;
cursor: pointer;
font-size: inherit;
font-family: inherit;
}
.button:hover {
color: cornflowerblue;
}
A list of uploaded files, a list of uploading files and a button to upload more files.
```js
const file = () => ({
name: faker.system.commonFileName(),
type: faker.system.commonFileType(),
size: faker.random.number(),
});
const value = [
file(),
file(),
file()
];
<Files
value={value}
uploadFile={file => new XMLHttpRequest()}/>
```
import React from 'react'
import UploadingFile from '../atoms/UploadingFile'
// TODO: retry on error
// TODO: make this a HOC for <UploadingFile>?
class Upload extends React.Component {
state = {
progress: 0,
error: undefined,
}
componentDidMount () {
const { request } = this.props
request.addEventListener('progress', this.handleProgress)
request.addEventListener('load', this.handleLoad)
request.addEventListener('error', this.handleError)
}
handleProgress = event => {
if (!event.lengthComputable) return
this.setState({
progress: event.loaded / event.total
})
}
handleLoad = event => {
if (this.props.request.status === 200) {
this.setState({
progress: 1
})
this.props.onComplete() // TODO: pass info?
} else {
this.setState({
error: 'There was an error'
})
}
}
handlError = event => {
this.setState({
error: 'There was an error'
})
}
render () {
const { file } = this.props
const { progress, error } = this.state
return (
<UploadingFile
file={file}
progress={progress}
error={error}/>
)
}
}
export default Upload
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment