diff --git a/packages/component-wizard/src/components/FileDropzone.js b/packages/component-wizard/src/components/FileDropzone.js index b97ee67cca4c8f93d4932358cfc1675b3e0628de..8fe3b9a53d3810be6b77aa68a94613289a816643 100644 --- a/packages/component-wizard/src/components/FileDropzone.js +++ b/packages/component-wizard/src/components/FileDropzone.js @@ -3,24 +3,10 @@ import classnames from 'classnames' import classes from './FileDropzone.local.scss' -const FileDropzone = ({ ...props }) => ( +const FileDropzone = ({ label }) => ( <div className={classnames(classes.dropzone)}> - <span>Drag items here or use the upload button</span> + <span>{label}</span> </div> ) -// export default compose( -// DropTarget( -// 'item', -// { -// drop(props) { -// console.log('s-a dat drop', props) -// }, -// }, -// (connect, monitor) => ({ -// connectDropTarget: connect.dropTarget(), -// }), -// ), -// )(FileDropzone) - export default FileDropzone diff --git a/packages/component-wizard/src/components/FilePicker.js b/packages/component-wizard/src/components/FilePicker.js index 860773a2540513c42372ad642c7341e7afc2ae32..779c42ec7535994bf253f512dd6c6a87167c2673 100644 --- a/packages/component-wizard/src/components/FilePicker.js +++ b/packages/component-wizard/src/components/FilePicker.js @@ -19,7 +19,7 @@ class FilePicker extends Component { } render() { - const { children } = this.props + const { children, disabled } = this.props return ( <div> <input @@ -32,7 +32,7 @@ class FilePicker extends Component { {React.cloneElement(children, { onClick: e => { e.preventDefault() - this.fileInput.click() + !disabled && this.fileInput.click() }, })} </div> diff --git a/packages/component-wizard/src/components/FileSection.js b/packages/component-wizard/src/components/FileSection.js index 43d29eaa0759b4dc87699aebd1d1a08df80956a3..c1c7c3bb46c456d859b8d2b5387e7e7fbb4f1005 100644 --- a/packages/component-wizard/src/components/FileSection.js +++ b/packages/component-wizard/src/components/FileSection.js @@ -3,8 +3,8 @@ import PropTypes from 'prop-types' import classnames from 'classnames' import { Icon } from '@pubsweet/ui' import { DropTarget } from 'react-dnd' -import { compose, getContext } from 'recompose' import { NativeTypes } from 'react-dnd-html5-backend' +import { compose, getContext, withHandlers, withState } from 'recompose' import { SortableList } from 'pubsweet-components-faraday/src/components' import FileItem from './FileItem' @@ -21,6 +21,7 @@ const DragHandle = () => ( ) const FileSection = ({ + error, title, files, listId, @@ -37,6 +38,8 @@ const FileSection = ({ connectDropTarget, allowedFileExtensions, isFetching, + canDropFile, + disabledFilepicker, }) => connectFileDrop( connectDropTarget( @@ -49,21 +52,32 @@ const FileSection = ({ })} > <div className={classnames(classes.header)}> - <span className={classnames(classes.title)}>{title}</span> - {!isFetching[listId] ? ( - <FilePicker - allowedFileExtensions={allowedFileExtensions} - onUpload={addFile} - > - <div className={classnames(classes['upload-button'])}> - <Icon>file-plus</Icon> + <div className={classnames(classes['picker-container'])}> + <span className={classnames(classes.title)}>{title}</span> + {!isFetching[listId] ? ( + <FilePicker + allowedFileExtensions={allowedFileExtensions} + disabled={disabledFilepicker()} + onUpload={addFile} + > + <div + className={classnames({ + [classes['upload-button']]: true, + [classes['disabled-picker']]: disabledFilepicker(), + })} + > + <Icon color={disabledFilepicker() ? '#999' : '#333'}> + file-plus + </Icon> + </div> + </FilePicker> + ) : ( + <div className={classnames(classes.rotate, classes.icon)}> + <Icon size={16}>loader</Icon> </div> - </FilePicker> - ) : ( - <div className={classnames(classes.rotate, classes.icon)}> - <Icon size={16}>loader</Icon> - </div> - )} + )} + </div> + <span className={classnames(classes.error)}>{error}</span> </div> <SortableList beginDragProps={['id', 'index', 'name', 'listId']} @@ -75,7 +89,7 @@ const FileSection = ({ moveItem={moveItem} removeFile={removeFile} /> - <FileDropzone /> + <FileDropzone label="Drag files here or use the add button." /> </div>, ), ) @@ -84,15 +98,35 @@ export default compose( getContext({ isFetching: PropTypes.object, }), + withState('error', 'setError', ''), + withHandlers({ + clearError: ({ setError }) => () => { + setError(e => '') + }, + }), + withHandlers({ + setError: ({ setError, clearError }) => err => { + setError(e => err, () => setTimeout(clearError, 3000)) + }, + disabledFilepicker: ({ files, maxFiles }) => () => files.length >= maxFiles, + }), DropTarget( 'item', { - drop({ changeList, listId: toListId }, monitor) { + drop( + { changeList, listId: toListId, maxFiles, files, setError }, + monitor, + ) { const { listId: fromListId, id } = monitor.getItem() + if (files.length >= maxFiles) { + setError('No more files of this type can be added.') + return + } + if (toListId === fromListId) return changeList(fromListId, toListId, id) }, - canDrop({ listId: toListId }, monitor) { + canDrop({ listId: toListId, setError }, monitor) { const { listId: fromListId } = monitor.getItem() return toListId !== fromListId }, @@ -106,18 +140,32 @@ export default compose( DropTarget( NativeTypes.FILE, { - drop({ addFile, allowedFileExtensions }, monitor) { + drop( + { files, maxFiles, addFile, allowedFileExtensions, setError }, + monitor, + ) { const [file] = monitor.getItem().files const fileExtention = file.name.split('.')[1] - if (allowedFileExtensions.includes(fileExtention)) { + if (files.length >= maxFiles) { + setError('No more files of this type can be added.') + return + } + + if ( + allowedFileExtensions && + allowedFileExtensions.includes(fileExtention) + ) { addFile(file) + } else { + setError('File type not allowed for these kind of files.') } }, }, (connect, monitor) => ({ connectFileDrop: connect.dropTarget(), isFileOver: monitor.isOver(), + canDropFile: monitor.canDrop(), }), ), )(FileSection) diff --git a/packages/component-wizard/src/components/FileSection.local.scss b/packages/component-wizard/src/components/FileSection.local.scss index 19102aa825bae5b93602e989f5febb5f36ae052d..6f1429c741c0d8b4a020a7bd0e0f48efc30594e6 100644 --- a/packages/component-wizard/src/components/FileSection.local.scss +++ b/packages/component-wizard/src/components/FileSection.local.scss @@ -9,12 +9,28 @@ display: flex; justify-content: flex-start; + .picker-container { + align-items: center; + display: flex; + flex: 1; + } + + .error { + color: firebrick; + font-size: 14px; + margin-right: 5px; + } + .upload-button { cursor: pointer; display: flex; margin-left: 5px; } + .disabled-picker { + cursor: default; + } + .title { margin: 5px; text-transform: uppercase; diff --git a/packages/component-wizard/src/components/Files.js b/packages/component-wizard/src/components/Files.js index 8847a84f98f862d9ea07e08da1d603c2067ecbc7..a79666c8ccbf3d1be69d5a9d61ba6cd9525bef22 100644 --- a/packages/component-wizard/src/components/Files.js +++ b/packages/component-wizard/src/components/Files.js @@ -41,6 +41,7 @@ const Files = ({ files={get(files, 'manuscripts') || []} isFirst listId="manuscripts" + maxFiles={Number.POSITIVE_INFINITY} moveItem={moveItem('manuscripts')} removeFile={removeFile('manuscripts')} title="Main manuscript" @@ -50,6 +51,7 @@ const Files = ({ changeList={changeList} files={get(files, 'supplementary') || []} listId="supplementary" + maxFiles={Number.POSITIVE_INFINITY} moveItem={moveItem('supplementary')} removeFile={removeFile('supplementary')} title="Supplementarry files" @@ -61,6 +63,7 @@ const Files = ({ files={get(files, 'coverLetter') || []} isLast listId="coverLetter" + maxFiles={1} moveItem={moveItem('coverLetter')} removeFile={removeFile('coverLetter')} title="Cover letter" diff --git a/packages/component-wizard/src/components/WizardStep.js b/packages/component-wizard/src/components/WizardStep.js index 9f0a4b8c51bffcc518b3f4c9154ccfc86d0e7865..52b7609e4776cb2d1453fbeb0ad8d281f8bc39b6 100644 --- a/packages/component-wizard/src/components/WizardStep.js +++ b/packages/component-wizard/src/components/WizardStep.js @@ -66,7 +66,6 @@ export default ({ ) }, )} - <Files /> <div className={classnames(classes.buttons)}> <Button onClick={isFirst ? () => history.push('/') : prevStep}> {isFirst diff --git a/packages/component-wizard/src/components/index.js b/packages/component-wizard/src/components/index.js index 276990a4799cd9b46ff99e66ecfe34e15ca1388d..0c688b4063ed20527e1eb26cf90fbf1f54adddc8 100644 --- a/packages/component-wizard/src/components/index.js +++ b/packages/component-wizard/src/components/index.js @@ -1,3 +1,4 @@ +export { default as Files } from './Files' export { default as Wizard } from './Wizard' export { default as Progress } from './Progress' export { default as WizardPage } from './WizardPage' diff --git a/packages/xpub-faraday/app/config/journal/submit-wizard.js b/packages/xpub-faraday/app/config/journal/submit-wizard.js index c6fa3d80784cc941bb71a6c6c95251f63ad76ca5..df734e05f83068a08aaf7a14be78a2aade3483f1 100644 --- a/packages/xpub-faraday/app/config/journal/submit-wizard.js +++ b/packages/xpub-faraday/app/config/journal/submit-wizard.js @@ -9,6 +9,7 @@ import { } from '@pubsweet/ui' import uploadFileFn from 'xpub-upload' import { required, minChars, minSize } from 'xpub-validators' +import { Files } from 'pubsweet-component-wizard/src/components/' import { AuthorList } from 'pubsweet-components-faraday/src/components' import { declarations } from './' @@ -177,35 +178,39 @@ export default { title: '4. Manuscript Files Upload', children: [ { - fieldId: 'label-manuscript', - renderComponent: Label, - label: 'Main Manuscript', - }, - { - fieldId: 'files.manuscripts', - label: 'Main Manuscript', - renderComponent: Supplementary, - }, - { - fieldId: 'label-supplementary', - renderComponent: Label, - label: 'Supplemental Files', - }, - { - fieldId: 'files.supplementary', - label: 'Supplemental Files', - renderComponent: Supplementary, - }, - { - fieldId: 'label-cover', - renderComponent: Label, - label: 'Cover Letter', - }, - { - fieldId: 'files.coverLetter', - label: 'Cover Letter', - renderComponent: Supplementary, - }, + fieldId: 'file-upload', + renderComponent: Files, + }, + // { + // fieldId: 'label-manuscript', + // renderComponent: Label, + // label: 'Main Manuscript', + // }, + // { + // fieldId: 'files.manuscripts', + // label: 'Main Manuscript', + // renderComponent: Supplementary, + // }, + // { + // fieldId: 'label-supplementary', + // renderComponent: Label, + // label: 'Supplemental Files', + // }, + // { + // fieldId: 'files.supplementary', + // label: 'Supplemental Files', + // renderComponent: Supplementary, + // }, + // { + // fieldId: 'label-cover', + // renderComponent: Label, + // label: 'Cover Letter', + // }, + // { + // fieldId: 'files.coverLetter', + // label: 'Cover Letter', + // renderComponent: Supplementary, + // }, ], }, ],