From acee298f6575941bcc381bf62003813ffa765b4c Mon Sep 17 00:00:00 2001 From: Alexandru Munteanu <alexandru.munt@gmail.com> Date: Tue, 21 Aug 2018 08:54:36 +0300 Subject: [PATCH] feat(visual): file section component --- packages/component-faraday-ui/src/File.js | 27 +++++--- .../component-faraday-ui/src/FileSection.js | 48 +++++++++++-- .../component-faraday-ui/src/FileSection.md | 69 +++++++++++-------- .../component-faraday-ui/src/SortableList.md | 2 +- .../component-faraday-ui/src/helpers/index.js | 2 + .../src/helpers/withFileSectionDrop.js | 45 ++++++++++++ .../src/helpers/withNativeFileDrop.js | 34 +++++++++ packages/styleguide/src/Wrapper.js | 6 +- .../styleguide/src/withDragDropContext.js | 4 ++ packages/styleguide/styleguide.config.js | 4 ++ 10 files changed, 197 insertions(+), 44 deletions(-) create mode 100644 packages/component-faraday-ui/src/helpers/index.js create mode 100644 packages/component-faraday-ui/src/helpers/withFileSectionDrop.js create mode 100644 packages/component-faraday-ui/src/helpers/withNativeFileDrop.js create mode 100644 packages/styleguide/src/withDragDropContext.js diff --git a/packages/component-faraday-ui/src/File.js b/packages/component-faraday-ui/src/File.js index a9c5252ef..5b870944b 100644 --- a/packages/component-faraday-ui/src/File.js +++ b/packages/component-faraday-ui/src/File.js @@ -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) diff --git a/packages/component-faraday-ui/src/FileSection.js b/packages/component-faraday-ui/src/FileSection.js index 2590b3a1c..3901be09f 100644 --- a/packages/component-faraday-ui/src/FileSection.js +++ b/packages/component-faraday-ui/src/FileSection.js @@ -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')}; diff --git a/packages/component-faraday-ui/src/FileSection.md b/packages/component-faraday-ui/src/FileSection.md index e67f554e0..c3a1b5f9e 100644 --- a/packages/component-faraday-ui/src/FileSection.md +++ b/packages/component-faraday-ui/src/FileSection.md @@ -1,4 +1,4 @@ -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> ``` diff --git a/packages/component-faraday-ui/src/SortableList.md b/packages/component-faraday-ui/src/SortableList.md index 4db4cfa6c..33a51c4ba 100644 --- a/packages/component-faraday-ui/src/SortableList.md +++ b/packages/component-faraday-ui/src/SortableList.md @@ -51,5 +51,5 @@ class Example extends React.Component { } } -<Example /> +;<Example /> ``` diff --git a/packages/component-faraday-ui/src/helpers/index.js b/packages/component-faraday-ui/src/helpers/index.js new file mode 100644 index 000000000..52df3f99c --- /dev/null +++ b/packages/component-faraday-ui/src/helpers/index.js @@ -0,0 +1,2 @@ +export { default as withNativeFileDrop } from './withNativeFileDrop' +export { default as withFileSectionDrop } from './withFileSectionDrop' diff --git a/packages/component-faraday-ui/src/helpers/withFileSectionDrop.js b/packages/component-faraday-ui/src/helpers/withFileSectionDrop.js new file mode 100644 index 000000000..ce997caf0 --- /dev/null +++ b/packages/component-faraday-ui/src/helpers/withFileSectionDrop.js @@ -0,0 +1,45 @@ +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(), + }), +) diff --git a/packages/component-faraday-ui/src/helpers/withNativeFileDrop.js b/packages/component-faraday-ui/src/helpers/withNativeFileDrop.js new file mode 100644 index 000000000..ab635fdbf --- /dev/null +++ b/packages/component-faraday-ui/src/helpers/withNativeFileDrop.js @@ -0,0 +1,34 @@ +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(), + }), +) diff --git a/packages/styleguide/src/Wrapper.js b/packages/styleguide/src/Wrapper.js index b4b841f14..2fa36592a 100644 --- a/packages/styleguide/src/Wrapper.js +++ b/packages/styleguide/src/Wrapper.js @@ -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) diff --git a/packages/styleguide/src/withDragDropContext.js b/packages/styleguide/src/withDragDropContext.js new file mode 100644 index 000000000..4eaa72da2 --- /dev/null +++ b/packages/styleguide/src/withDragDropContext.js @@ -0,0 +1,4 @@ +import { DragDropContext } from 'react-dnd' +import HTML5Backend from 'react-dnd-html5-backend' + +export default DragDropContext(HTML5Backend) diff --git a/packages/styleguide/styleguide.config.js b/packages/styleguide/styleguide.config.js index 74f9330bd..b0d7ed608 100644 --- a/packages/styleguide/styleguide.config.js +++ b/packages/styleguide/styleguide.config.js @@ -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'), }, -- GitLab