134
app/routes/Forms/Dropzone/Dropzone.js
Executable file
134
app/routes/Forms/Dropzone/Dropzone.js
Executable file
@@ -0,0 +1,134 @@
|
||||
import React from 'react';
|
||||
import classNames from 'classnames';
|
||||
import FileDrop from 'react-dropzone';
|
||||
import _ from 'lodash';
|
||||
|
||||
import {
|
||||
Container,
|
||||
Divider,
|
||||
Badge,
|
||||
Button,
|
||||
ButtonGroup
|
||||
} from './../../../components';
|
||||
import {
|
||||
FilesGrid,
|
||||
FilesList
|
||||
} from './components';
|
||||
|
||||
import { HeaderMain } from "../../components/HeaderMain";
|
||||
|
||||
export class Dropzone extends React.Component {
|
||||
state = {
|
||||
isOver: false,
|
||||
files: [],
|
||||
listStyle: 'grid'
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isOver, files, listStyle } = this.state;
|
||||
const dropzoneClass = classNames({
|
||||
'dropzone--active': isOver
|
||||
}, 'dropzone');
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<HeaderMain
|
||||
title="Dropzone"
|
||||
className="mb-5 mt-4"
|
||||
/>
|
||||
{ /* DropZone */ }
|
||||
<div className="mb-4">
|
||||
<p className="mb-3">
|
||||
Simple HTML5-compliant drag'n'drop zone for files built with React.js.
|
||||
</p>
|
||||
<FileDrop
|
||||
multiple
|
||||
onDragEnter={() => { this.setState({isOver: true}) }}
|
||||
onDragLeave={() => { this.setState({isOver: false}) }}
|
||||
onDrop={this._filesDropped}
|
||||
>
|
||||
{
|
||||
({ getRootProps, getInputProps }) => (
|
||||
<div {...getRootProps()} className={dropzoneClass}>
|
||||
<i className="fa fa-cloud-upload fa-fw fa-3x mb-3"></i>
|
||||
<h5 className='mt-0'>
|
||||
Upload Your files
|
||||
</h5>
|
||||
<p>
|
||||
Drag a file here or <span className='text-primary'>browse</span> for a file to upload.
|
||||
</p>
|
||||
<p className="small">
|
||||
JPG, GIF, PNG, MOV, and AVI. Please choose files under 2GB for upload. File sizes are 400x300px.
|
||||
</p>
|
||||
<input { ...getInputProps() } />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
</FileDrop>
|
||||
</div>
|
||||
{ /* Files List */}
|
||||
{
|
||||
files.length > 0 && (
|
||||
<div className="mt-2">
|
||||
<div className="d-flex">
|
||||
<Divider
|
||||
position="left"
|
||||
className="flex-shrink-1 flex-grow-1"
|
||||
>
|
||||
<div className="px-2">
|
||||
Attachments
|
||||
|
||||
<Badge
|
||||
className="ml-1 text-white"
|
||||
pill
|
||||
color="secondary"
|
||||
>
|
||||
{ files.length }
|
||||
</Badge>
|
||||
</div>
|
||||
</Divider>
|
||||
<ButtonGroup className="flex-grow-0 flex-shrink-0 pl-2">
|
||||
<Button
|
||||
active={ listStyle === 'list' }
|
||||
onClick={() => {this.setState({listStyle: 'list'})}}
|
||||
size="sm"
|
||||
outline
|
||||
>
|
||||
<i className='fa fa-bars fa-fw'></i>
|
||||
</Button>
|
||||
<Button
|
||||
active={ listStyle === 'grid' }
|
||||
onClick={() => {this.setState({listStyle: 'grid'})}}
|
||||
size="sm"
|
||||
outline
|
||||
>
|
||||
<i className='fa fa-th-large fa-fw'></i>
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
{
|
||||
listStyle === 'grid' ?
|
||||
<FilesGrid files={ files } onFileRemove={this._removeFile} /> :
|
||||
<FilesList files={ files } onFileRemove={this._removeFile} />
|
||||
}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
</Container>
|
||||
);
|
||||
}
|
||||
|
||||
_filesDropped = (files) => {
|
||||
this.setState({
|
||||
files: [...this.state.files, ...files],
|
||||
isOver: false
|
||||
})
|
||||
}
|
||||
|
||||
_removeFile = (file) => {
|
||||
this.setState({
|
||||
files: _.reject(this.state.files, file)
|
||||
})
|
||||
}
|
||||
}
|
64
app/routes/Forms/Dropzone/components/FilesGrid.js
Executable file
64
app/routes/Forms/Dropzone/components/FilesGrid.js
Executable file
@@ -0,0 +1,64 @@
|
||||
import React from 'react';
|
||||
import _ from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import numeral from 'numeral';
|
||||
import moment from 'moment';
|
||||
|
||||
import {
|
||||
Col,
|
||||
Row,
|
||||
Card,
|
||||
CardBody,
|
||||
Button,
|
||||
UncontrolledTooltip
|
||||
} from './../../../../components';
|
||||
import classes from './common.scss';
|
||||
import {
|
||||
getFileIcon
|
||||
} from './../utilities';
|
||||
|
||||
export const FilesGrid = ({ files, onFileRemove }) => (
|
||||
<Row className="mt-4">
|
||||
{
|
||||
_.map(files, (file, index) => (
|
||||
<Col lg={ 4 } md={ 6 } key={index}>
|
||||
<Card className="mb-3">
|
||||
<div className={ classNames("card-img-top", classes['ph--large']) }>
|
||||
<i className={`fa fa-fw fa-3x ${getFileIcon(file)}`} />
|
||||
</div>
|
||||
<CardBody className="pt-2">
|
||||
<div className="d-flex align-items-center mb-0 mt-0">
|
||||
<h6 className="text-truncate mb-0">
|
||||
{ file.name }
|
||||
</h6>
|
||||
<Button
|
||||
color="link"
|
||||
onClick={() => {onFileRemove(file)}}
|
||||
size="sm"
|
||||
id={`delete-file-${index}`}
|
||||
>
|
||||
<i className="fa fa-times fa-fw text-danger"></i>
|
||||
</Button>
|
||||
<UncontrolledTooltip placement="left" target={`delete-file-${index}`}>
|
||||
Delete File
|
||||
</UncontrolledTooltip>
|
||||
</div>
|
||||
<div className="mb-0">
|
||||
by You · <span className='text-uppercase'>{`${numeral(file.size).format('0.00a')}B`}</span>
|
||||
</div>
|
||||
<div className='mb-0'>
|
||||
{ moment(file.modifiedDate).format('DD-MMM-YYYY, HH:mm') }
|
||||
</div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
</Col>
|
||||
))
|
||||
}
|
||||
</Row>
|
||||
);
|
||||
|
||||
FilesGrid.propTypes = {
|
||||
files: PropTypes.array,
|
||||
onFileRemove: PropTypes.func
|
||||
}
|
73
app/routes/Forms/Dropzone/components/FilesList.js
Executable file
73
app/routes/Forms/Dropzone/components/FilesList.js
Executable file
@@ -0,0 +1,73 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import _ from 'lodash';
|
||||
import numeral from 'numeral';
|
||||
import moment from 'moment';
|
||||
|
||||
import {
|
||||
Table,
|
||||
Button,
|
||||
UncontrolledTooltip
|
||||
} from './../../../../components';
|
||||
import classes from './common.scss';
|
||||
import {
|
||||
getFileIcon
|
||||
} from './../utilities';
|
||||
|
||||
export const FilesList = ({ files, onFileRemove }) => (
|
||||
<Table responsive hover className="mt-3">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="bt-0"></th>
|
||||
<th className="bt-0">File Name</th>
|
||||
<th className="bt-0">Size</th>
|
||||
<th className="bt-0">Owner</th>
|
||||
<th className="bt-0">Modified Date</th>
|
||||
<th className="bt-0 text-right">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{
|
||||
_.map(files, (file, index) => (
|
||||
<tr key={ index }>
|
||||
<td className="align-middle">
|
||||
<div className={ classes['ph--small'] }>
|
||||
<i className={`fa fa-fw fa-2x ${getFileIcon(file)}`} />
|
||||
</div>
|
||||
</td>
|
||||
<td className="align-middle">
|
||||
{ file.name }
|
||||
</td>
|
||||
<td className="align-middle text-uppercase">
|
||||
{ numeral(file.size).format('0.00a') }B
|
||||
</td>
|
||||
<td className="align-middle">
|
||||
You
|
||||
</td>
|
||||
<td className="align-middle">
|
||||
{ moment(file.modifiedDate).format('DD-MMM-YYYY, HH:mm') }
|
||||
</td>
|
||||
<td className="text-right align-middle">
|
||||
<Button
|
||||
color="link"
|
||||
onClick={() => {onFileRemove(file)}}
|
||||
size="sm"
|
||||
id={`delete-file-${index}`}
|
||||
>
|
||||
<i className="fa fa-times fa-fw text-danger"></i>
|
||||
</Button>
|
||||
<UncontrolledTooltip placement="left" target={`delete-file-${index}`}>
|
||||
Delete File
|
||||
</UncontrolledTooltip>
|
||||
</td>
|
||||
</tr>
|
||||
))
|
||||
}
|
||||
</tbody>
|
||||
</Table>
|
||||
);
|
||||
|
||||
FilesList.propTypes = {
|
||||
files: PropTypes.array,
|
||||
onFileRemove: PropTypes.func
|
||||
}
|
25
app/routes/Forms/Dropzone/components/common.scss
Executable file
25
app/routes/Forms/Dropzone/components/common.scss
Executable file
@@ -0,0 +1,25 @@
|
||||
@import "./../../../../styles/variables.scss";
|
||||
|
||||
.ph--large {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 200px;
|
||||
background-color: $gray-400;
|
||||
margin-bottom: 5px;
|
||||
|
||||
> i {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.ph--small {
|
||||
display: inline-block;
|
||||
background-color: $gray-400;
|
||||
padding: 7px 5px;
|
||||
border-radius: 7px;
|
||||
|
||||
> i {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
2
app/routes/Forms/Dropzone/components/index.js
Executable file
2
app/routes/Forms/Dropzone/components/index.js
Executable file
@@ -0,0 +1,2 @@
|
||||
export * from './FilesGrid';
|
||||
export * from './FilesList';
|
3
app/routes/Forms/Dropzone/index.js
Executable file
3
app/routes/Forms/Dropzone/index.js
Executable file
@@ -0,0 +1,3 @@
|
||||
import { Dropzone } from './Dropzone';
|
||||
|
||||
export default Dropzone;
|
32
app/routes/Forms/Dropzone/utilities.js
Executable file
32
app/routes/Forms/Dropzone/utilities.js
Executable file
@@ -0,0 +1,32 @@
|
||||
export const typeToIcon = type => {
|
||||
const map = {
|
||||
['application/msword']: 'fa-file-word-o',
|
||||
['application/excel']: 'fa-file-excel-o',
|
||||
['application/vnd.oasis.opendocument.spreadsheet']: 'fa-file-excel-o',
|
||||
['application/vnd.oasis.opendocument.presentation']: 'fa-file-powerpoint-o',
|
||||
['application/mspowerpoint']: 'fa-file-powerpoint-o',
|
||||
['application/x-zip-compressed']: 'fa-file-archive-o',
|
||||
['image/jpeg']: 'fa-file-image-o',
|
||||
['image/png']: 'fa-file-image-o',
|
||||
['audio/mp3']: 'fa-file-audio-o',
|
||||
['text/plain']: 'fa-file-text-o'
|
||||
}
|
||||
return map[type] || null;
|
||||
}
|
||||
|
||||
export const extToIcon = filename => {
|
||||
const map = {
|
||||
['doc']: 'fa-file-word-o',
|
||||
['docx']: 'fa-file-word-o',
|
||||
['xls']: 'fa-file-excel-o',
|
||||
['xlsx']: 'fa-file-excel-o',
|
||||
['ppt']: 'fa-file-powerpoint-o',
|
||||
['pdf']: 'fa-file-pdf-o'
|
||||
}
|
||||
|
||||
return map[filename.split('.').pop()] || null;
|
||||
}
|
||||
|
||||
export const getFileIcon = file => {
|
||||
return typeToIcon(file.type) || extToIcon(file.name) || 'fa-file-o';
|
||||
}
|
Reference in New Issue
Block a user