Skip to content
Snippets Groups Projects
Commit acee298f authored by Alexandru Munteanu's avatar Alexandru Munteanu
Browse files

feat(visual): file section component

parent cf685441
No related branches found
No related tags found
1 merge request!43Sprint #19
......@@ -31,12 +31,13 @@ const hasPreview = (name = '') => {
}
const FileItem = ({
item: file,
fileSize,
onPreview,
item: file,
removeFile,
hasPreview,
onDownload,
onDelete,
dragHandle = null,
...rest
}) => (
......@@ -49,21 +50,22 @@ const FileItem = ({
{hasPreview && (
<IconButton
icon="eye"
iconSize={3}
iconSize={2}
ml={1}
mr={1}
onClick={onPreview(file)}
onClick={onPreview}
secondary
/>
)}
<IconButton
icon="download"
iconSize={3}
ml={1}
iconSize={2}
ml={hasPreview ? 0 : 1}
mr={1}
onClick={onDownload(file)}
onClick={onDownload}
secondary
/>
<IconButton icon="trash" iconSize={2} mr={1} onClick={onDelete} secondary />
</Root>
)
......@@ -80,6 +82,8 @@ FileItem.propTypes = {
onPreview: PropTypes.func,
/** Handler for the download button. */
onDownload: PropTypes.func,
/** Handler for the delete button. */
onDelete: PropTypes.func,
}
export default compose(
......@@ -88,11 +92,14 @@ export default compose(
fileSize: parseFileSize(size),
})),
withHandlers({
onDownload: ({ onDownload }) => file => () => {
onDownload(file)
onDownload: ({ onDownload, item }) => () => {
typeof onDownload === 'function' && onDownload(item)
},
onPreview: ({ onPreview, item }) => () => {
typeof onPreview === 'function' && onPreview(item)
},
onPreview: ({ onPreview }) => file => () => {
onPreview(file)
onDelete: ({ onDelete, item }) => () => {
typeof onDelete === 'function' && onDelete(item)
},
}),
)(FileItem)
......
......@@ -2,9 +2,10 @@ import React from 'react'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit'
import { FilePicker } from '@pubsweet/ui'
import { compose, withProps } from 'recompose'
import { compose, withStateHandlers, withProps } from 'recompose'
import { radiusHelpers } from './styledHelpers'
import { withNativeFileDrop, withFileSectionDrop } from './helpers'
import {
Row,
Item,
......@@ -24,21 +25,41 @@ const EXTENSIONS = {
}
const FileSection = ({
error,
title,
isLast,
listId,
isFirst,
required,
files = [],
moveItem,
isFileItemOver,
canDropFileItem,
connectFileDrop,
supportedFormats,
connectDropTarget,
allowedFileExtensions,
files = [],
onFilePick = () => {},
onPreview,
onDownload,
onDelete,
}) => (
<Root isFirst={isFirst} isLast={isLast}>
<Root
innerRef={instance => {
connectFileDrop(instance)
connectDropTarget(instance)
}}
isFileItemOver={isFileItemOver && canDropFileItem}
isFirst={isFirst}
isLast={isLast}
>
{error}
<Row alignItems="center">
<Item>
<Label required={required}>{title}</Label>
<FilePicker
allowedFileExtensions={allowedFileExtensions}
onUpload={file => {}}
onUpload={onFilePick}
>
<ActionLink icon="plus" size="small">
UPLOAD FILE
......@@ -54,25 +75,42 @@ const FileSection = ({
)}
</Row>
<SortableList
beginDragProps={['id', 'index', 'name', 'listId']}
dragHandle={DragHandle}
items={files}
listId={listId}
listItem={FileItem}
mb={1}
moveItem={moveItem}
onDelete={onDelete}
onDownload={onDownload}
onPreview={onPreview}
/>
</Root>
)
export default compose(
withStateHandlers(
{ error: '' },
{
setError: () => error => ({
error,
}),
},
),
withProps(({ allowedFileExtensions = [] }) => ({
supportedFormats: allowedFileExtensions
.map(ext => EXTENSIONS[ext.toLowerCase()])
.join(', '),
})),
withFileSectionDrop,
withNativeFileDrop,
)(FileSection)
// #region styles
const Root = styled.div`
background: ${th('colorBackground')};
background: ${props =>
props.isFileItemOver ? th('colorFurniture') : th('colorBackground')};
min-height: calc(${th('gridUnit')} * 22);
padding: 0 ${th('gridUnit')};
......
A section that shows FileItems. Drag and drop support.
Sections on top of each other.
```js
const files = [
......@@ -9,31 +9,7 @@ const files = [
},
{
id: 'file2',
name: 'myfile.docx',
size: 133127,
},
];
<FileSection
files={files}
title="Main Manuscript"
required
allowedFileExtensions={['pdf', 'doc', 'docx']}
/>
```
Multiple sections on top of each other.
```js
const files = [
{
id: 'file1',
name: 'myfile.docx',
size: 51312,
},
{
id: 'file2',
name: 'myfile.docx',
name: 'another_pdf.pdf',
size: 133127,
},
];
......@@ -42,16 +18,55 @@ const files = [
<FileSection
isFirst
required
listId="mainManuscript"
files={files}
title="Main Manuscript"
allowedFileExtensions={['pdf', 'doc', 'docx']}
onFileDrop={f => console.log('dropped a native file', f)}
onFilePick={f => console.log('picked a file', f)}
moveItem={
(dragIndex, hoverIndex) => console.log('moving the item from', dragIndex, hoverIndex)
}
changeList={
(from, to, fileId) => console.log('change from to', from, to, fileId)
}
onDelete={f => console.log('delete', f)}
onPreview={f => console.log('preview', f)}
onDownload={f => console.log('download', f)}
/>
<FileSection
required
title="Cover Letter"
files={files}
listId="coverLetter"
allowedFileExtensions={['pdf', 'doc', 'docx']}
onFileDrop={f => console.log('dropped a native file', f)}
onFilePick={f => console.log('picked a file', f)}
moveItem={
(dragIndex, hoverIndex) => console.log('moving the item from', dragIndex, hoverIndex)
}
changeList={
(from, to, fileId) => console.log('change from to', from, to, fileId)
}
onDelete={f => console.log('delete', f)}
onPreview={f => console.log('preview', f)}
onDownload={f => console.log('download', f)}
/>
<FileSection title="Supplimental Files" required isLast />
<FileSection
files={[]}
title="Supplimental Files"
listId="supplimentalFiles"
required isLast onFileDrop={f => console.log('dropped a native file', f)}
onFilePick={f => console.log('picked a file', f)}
moveItem={
(dragIndex, hoverIndex) => console.log('moving the item from', dragIndex, hoverIndex)
}
changeList={
(from, to, fileId) => console.log('change from to', from, to, fileId)
}
onDelete={f => console.log('delete', f)}
onPreview={f => console.log('preview', f)}
onDownload={f => console.log('download', f)}
/>
</div>
```
......@@ -51,5 +51,5 @@ class Example extends React.Component {
}
}
<Example />
;<Example />
```
export { default as withNativeFileDrop } from './withNativeFileDrop'
export { default as withFileSectionDrop } from './withFileSectionDrop'
import { DropTarget } from 'react-dnd'
export default DropTarget(
'item',
{
drop(
{
files,
maxFiles,
setError,
changeList,
listId: toListId,
allowedFileExtensions,
},
monitor,
) {
const { listId: fromListId, id, name } = monitor.getItem()
const fileExtention = name.split('.')[1]
if (
allowedFileExtensions &&
!allowedFileExtensions.includes(fileExtention)
) {
setError('Invalid file type.')
return
}
if (files.length >= maxFiles) {
setError('No more files can be added to this section.')
return
}
if (toListId === fromListId) return
changeList(fromListId, toListId, id)
},
canDrop({ listId: toListId, setError }, monitor) {
const { listId: fromListId } = monitor.getItem()
return toListId !== fromListId
},
},
(connect, monitor) => ({
isFileItemOver: monitor.isOver(),
canDropFileItem: monitor.canDrop(),
connectDropTarget: connect.dropTarget(),
}),
)
import { DropTarget } from 'react-dnd'
import { NativeTypes } from 'react-dnd-html5-backend'
export default DropTarget(
NativeTypes.FILE,
{
drop(
{ onFileDrop, files, maxFiles, setError, allowedFileExtensions },
monitor,
) {
const [file] = monitor.getItem().files
const fileExtention = file.name.split('.')[1]
if (files.length >= maxFiles) {
setError('No more files can be added to this section.')
return
}
if (
allowedFileExtensions &&
!allowedFileExtensions.includes(fileExtention)
) {
setError('Invalid file type.')
}
typeof onFileDrop === 'function' && onFileDrop(file)
},
},
(connect, monitor) => ({
isFileOver: monitor.isOver(),
canDropFile: monitor.canDrop(),
connectFileDrop: connect.dropTarget(),
}),
)
......@@ -6,13 +6,15 @@ import hindawiTheme from 'hindawi-theme'
import { ThemeProvider } from 'styled-components'
import { createStore, combineReducers } from 'redux'
import withDragDropContext from './withDragDropContext'
const store = createStore(
combineReducers({
form: reducer,
}),
)
export default class Wrapper extends Component {
class Wrapper extends Component {
render() {
return (
<Provider store={store}>
......@@ -23,3 +25,5 @@ export default class Wrapper extends Component {
)
}
}
export default withDragDropContext(Wrapper)
import { DragDropContext } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
export default DragDropContext(HTML5Backend)
......@@ -4,17 +4,21 @@ module.exports = {
sections: [
{
name: 'Hinadwi UI',
sectionDepth: 1,
components: ['../component-faraday-ui/src/[A-Z]*.js'],
},
{
name: 'Modals',
sectionDepth: 1,
components: ['../component-faraday-ui/src/modals/[A-Z]*.js'],
},
{
name: 'Grid Items',
sectionDepth: 1,
components: ['../component-faraday-ui/src/gridItems/[A-Z]*.js'],
},
],
pagePerSection: true,
styleguideComponents: {
Wrapper: path.join(__dirname, 'src/Wrapper'),
},
......
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