diff --git a/packages/component-faraday-ui/src/File.js b/packages/component-faraday-ui/src/File.js index a0f04d2d92dec5e7556868bc7b2f3a29010682c9..a9c5252ef4b49b036e61ad229b1a40465e1571e5 100644 --- a/packages/component-faraday-ui/src/File.js +++ b/packages/component-faraday-ui/src/File.js @@ -31,7 +31,7 @@ const hasPreview = (name = '') => { } const FileItem = ({ - file, + item: file, fileSize, onPreview, removeFile, @@ -69,7 +69,7 @@ const FileItem = ({ FileItem.propTypes = { /** The file. */ - file: PropTypes.shape({ + item: PropTypes.shape({ id: PropTypes.string, name: PropTypes.string, size: PropTypes.number, @@ -83,7 +83,7 @@ FileItem.propTypes = { } export default compose( - withProps(({ file: { name, size } }) => ({ + withProps(({ name, size }) => ({ hasPreview: hasPreview(name), fileSize: parseFileSize(size), })), diff --git a/packages/component-faraday-ui/src/File.md b/packages/component-faraday-ui/src/File.md index 7fc933709c66ddd55eeecee3acbd0eabd8bcde53..615a0badc6168620dceaaa8708511340321c4059 100644 --- a/packages/component-faraday-ui/src/File.md +++ b/packages/component-faraday-ui/src/File.md @@ -2,7 +2,7 @@ A pdf file. ```js <FileItem - file={{ + item={{ id: 'file1', name: 'myfile.pdf', size: 1231312, @@ -16,7 +16,7 @@ A Word document (no preview available). ```js <FileItem - file={{ + item={{ id: 'file1', name: 'myfile.docx', size: 51312, @@ -30,7 +30,7 @@ With a drag handle. ```js <FileItem - file={{ + item={{ id: 'file1', name: 'myfile.pdf', size: 1231312, diff --git a/packages/component-faraday-ui/src/FileSection.js b/packages/component-faraday-ui/src/FileSection.js index b5aaa042184c2795ff663ed8a8dd85c65118d1b4..2590b3a1c8687fb9edc48b81fd27ae55dc88d594 100644 --- a/packages/component-faraday-ui/src/FileSection.js +++ b/packages/component-faraday-ui/src/FileSection.js @@ -1,10 +1,20 @@ import React from 'react' +import styled from 'styled-components' import { th } from '@pubsweet/ui-toolkit' import { FilePicker } from '@pubsweet/ui' -import styled, { css } from 'styled-components' import { compose, withProps } from 'recompose' -import { Label, ActionLink, Text, Row, Item, FileItem, DragHandle } from './' +import { radiusHelpers } from './styledHelpers' +import { + Row, + Item, + Text, + Label, + FileItem, + ActionLink, + DragHandle, + SortableList, +} from './' const EXTENSIONS = { pdf: 'PDF', @@ -18,6 +28,7 @@ const FileSection = ({ isLast, isFirst, required, + files = [], supportedFormats, allowedFileExtensions, }) => ( @@ -27,7 +38,7 @@ const FileSection = ({ <Label required={required}>{title}</Label> <FilePicker allowedFileExtensions={allowedFileExtensions} - onUpload={file => console.log('uploaded ', file)} + onUpload={file => {}} > <ActionLink icon="plus" size="small"> UPLOAD FILE @@ -42,22 +53,10 @@ const FileSection = ({ </Item> )} </Row> - <FileItem + <SortableList dragHandle={DragHandle} - file={{ - id: 'file1', - name: 'myfile.docx', - size: 51312, - }} - mb={1} - /> - <FileItem - dragHandle={DragHandle} - file={{ - id: 'file1', - name: 'myfile.docx', - size: 51312, - }} + items={files} + listItem={FileItem} mb={1} /> </Root> @@ -72,28 +71,6 @@ export default compose( )(FileSection) // #region styles -const radiusHelpers = props => { - const marginTop = props.isFirst - ? css` - border-top-left-radius: ${th('borderRadius')}; - border-top-right-radius: ${th('borderRadius')}; - ` - : css`` - - const marginBottom = props.isLast - ? css` - border-bottom-left-radius: ${th('borderRadius')}; - border-bottom-right-radius: ${th('borderRadius')}; - ` - : css`` - - return css` - border-radius: none; - ${marginTop}; - ${marginBottom}; - ` -} - const Root = styled.div` background: ${th('colorBackground')}; min-height: calc(${th('gridUnit')} * 22); diff --git a/packages/component-faraday-ui/src/FileSection.md b/packages/component-faraday-ui/src/FileSection.md index d9dd000a978cb801f3217df1cf7f4f2cf0f49891..e67f554e09999117936d7641e96e88c41b5789f6 100644 --- a/packages/component-faraday-ui/src/FileSection.md +++ b/packages/component-faraday-ui/src/FileSection.md @@ -1,7 +1,21 @@ A section that shows FileItems. Drag and drop support. ```js +const files = [ + { + id: 'file1', + name: 'myfile.docx', + size: 51312, + }, + { + id: 'file2', + name: 'myfile.docx', + size: 133127, + }, +]; + <FileSection + files={files} title="Main Manuscript" required allowedFileExtensions={['pdf', 'doc', 'docx']} @@ -11,16 +25,31 @@ A section that shows FileItems. Drag and drop support. Multiple sections on top of each other. ```js +const files = [ + { + id: 'file1', + name: 'myfile.docx', + size: 51312, + }, + { + id: 'file2', + name: 'myfile.docx', + size: 133127, + }, +]; + <div style={{ display: 'flex', flexDirection: 'column' }}> <FileSection - title="Main Manuscript" isFirst required + files={files} + title="Main Manuscript" allowedFileExtensions={['pdf', 'doc', 'docx']} /> <FileSection - title="Cover Letter" required + title="Cover Letter" + files={files} allowedFileExtensions={['pdf', 'doc', 'docx']} /> <FileSection title="Supplimental Files" required isLast /> diff --git a/packages/component-faraday-ui/src/SortableList.js b/packages/component-faraday-ui/src/SortableList.js index 294df130b7bd4159f36df4b9ac969cec926e44fd..1c6595d653c344dfae39953ab9ed0ecbe2153919 100644 --- a/packages/component-faraday-ui/src/SortableList.js +++ b/packages/component-faraday-ui/src/SortableList.js @@ -1,5 +1,152 @@ -import React from 'react' +/* eslint-disable react/forbid-prop-types */ +/* eslint-disable react/require-default-props */ +import React, { Fragment } from 'react' +import PropTypes from 'prop-types' +import { pick } from 'lodash' +import { findDOMNode } from 'react-dom' +import { compose, toClass } from 'recompose' +import { DragSource, DropTarget } from 'react-dnd' -const SortableList = () => <div>sortable items inside</div> +const itemSource = { + beginDrag(props) { + return pick(props, props.beginDragProps) + }, +} + +const itemTarget = { + hover({ moveItem, index, listId }, monitor, component) { + const { index: dragIndex, listId: toListId } = monitor.getItem() + const hoverIndex = index + + if (listId !== toListId) { + return + } + + if (dragIndex === hoverIndex) { + return + } + + const hoverBoundingRect = findDOMNode(component).getBoundingClientRect() // eslint-disable-line + const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2 + const clientOffset = monitor.getClientOffset() + const hoverClientY = clientOffset.y - hoverBoundingRect.top + + if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) { + return + } + + if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) { + return + } + if (typeof moveItem === 'function') { + moveItem(dragIndex, hoverIndex, monitor.getItem()) + } + monitor.getItem().index = hoverIndex + }, + drop({ dropItem, ...restProps }, monitor) { + if (dropItem && typeof dropItem === 'function') + dropItem(monitor.getItem(), restProps) + }, +} + +const Item = ({ + listItem, + dragHandle, + connectDragSource, + connectDropTarget, + connectDragPreview, + ...rest +}) => + dragHandle + ? connectDragPreview( + connectDropTarget( + <div style={{ flex: 1 }}> + {React.createElement(listItem, { + ...rest, + dragHandle: connectDragSource( + <div style={{ display: 'flex' }}> + {React.createElement(dragHandle)} + </div>, + ), + })} + </div>, + ), + ) + : connectDropTarget( + connectDragSource( + <div style={{ flex: 1 }}>{React.createElement(listItem, rest)}</div>, + ), + ) + +const DecoratedItem = compose( + DropTarget('item', itemTarget, (connect, monitor) => ({ + connectDropTarget: connect.dropTarget(), + isOver: monitor.isOver(), + })), + DragSource('item', itemSource, (connect, monitor) => ({ + connectDragSource: connect.dragSource(), + connectDragPreview: connect.dragPreview(), + isDragging: monitor.isDragging(), + })), + toClass, +)(Item) + +const SortableList = ({ + listItem, + dragHandle, + items = [], + itemKey = 'id', + ...rest +}) => ( + <Fragment> + {items.map((item, i) => ( + <DecoratedItem + dragHandle={dragHandle} + index={i} + item={item} + key={item[itemKey]} + listItem={listItem} + {...item} + {...rest} + /> + ))} + </Fragment> +) + +SortableList.propTypes = { + /** List items. */ + items: PropTypes.array, + /** Render prop for list's item. */ + listItem: PropTypes.oneOfType([PropTypes.element, PropTypes.func]).isRequired, + /** Key used to map through items. */ + itemKey: PropTypes.string, + /** Function invoked to change the order of the list's items. */ + moveItem: PropTypes.func, + /** Function invoked when the currently dragged item is dropped. */ + dropItem: PropTypes.func, + /** + * What props to pick from the dragged item. E.g.: if a specific property is needed + * in the move function. + * */ + beginDragProps: PropTypes.array, +} + +SortableList.moveItem = (items, dragIndex, hoverIndex) => { + if (!dragIndex) return items + if (dragIndex <= hoverIndex) { + return [ + ...items.slice(0, dragIndex), + items[hoverIndex], + items[dragIndex], + ...items.slice(hoverIndex + 1), + ] + } + return [ + ...items.slice(0, hoverIndex), + items[dragIndex], + items[hoverIndex], + ...items.slice(dragIndex + 1), + ] +} export default SortableList diff --git a/packages/component-faraday-ui/src/SortableList.md b/packages/component-faraday-ui/src/SortableList.md index 3302a88e0c13e18de6552e2fe75ba187dfda05cd..4db4cfa6c0d812d1235d1f4834b2897eba3e27d5 100644 --- a/packages/component-faraday-ui/src/SortableList.md +++ b/packages/component-faraday-ui/src/SortableList.md @@ -1,5 +1,55 @@ A list with drag and drop support. ```js -<SortableList /> -``` \ No newline at end of file +const items = [ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Michael', lastName: 'Jackson' }, + { firstName: 'David', lastName: 'Blaine' }, +] + +const Item = ({ dragHandle, isOver, isDragging, ...rest }) => ( + <div style={{ display: 'flex' }}> + {dragHandle} + <span> + {rest.firstName} {rest.lastName} + </span> + </div> +) + +class Example extends React.Component { + constructor(props) { + super(props) + + this.state = { + items: [ + { firstName: 'John', lastName: 'Doe' }, + { firstName: 'Michael', lastName: 'Jackson' }, + { firstName: 'David', lastName: 'Blaine' }, + ], + } + } + + render() { + return ( + <SortableList + items={this.state.items} + listItem={Item} + itemKey="firstName" + dragHandle={DragHandle} + moveItem={(dragIndex, hoverIndex) => { + this.setState({ + items: SortableList.moveItem( + this.state.items, + dragIndex, + hoverIndex, + ), + }) + }} + dropItem={(item, props) => {}} + /> + ) + } +} + +<Example /> +``` diff --git a/packages/component-faraday-ui/src/Tag.js b/packages/component-faraday-ui/src/Tag.js index 2093a3fc8e2e3c896286db988dcc500474b054c6..b1fae817f37176842e97e75b3b24000684507c82 100644 --- a/packages/component-faraday-ui/src/Tag.js +++ b/packages/component-faraday-ui/src/Tag.js @@ -1,7 +1,7 @@ import styled from 'styled-components' import { th } from '@pubsweet/ui-toolkit' -// #region styles +/** @component */ export default styled.div` background-color: ${props => props.status ? th('tag.statusBackgroundColor') : th('tag.backgroundColor')}; @@ -15,4 +15,3 @@ export default styled.div` text-transform: uppercase; width: fit-content; ` -// #endregion diff --git a/packages/component-faraday-ui/src/Text.js b/packages/component-faraday-ui/src/Text.js index f3663495b31f777d7621a0709ef7048e79d51bec..48d1330b0a9f674b9025725806e74c864b18b520 100644 --- a/packages/component-faraday-ui/src/Text.js +++ b/packages/component-faraday-ui/src/Text.js @@ -36,6 +36,7 @@ const fontSize = css` props.small ? th('lineHeightBaseSmall') : th('lineHeightBase')}; ` +/** @component */ export default styled.span` display: inline-block; font-style: ${props => props.fontStyle || 'normal'}; diff --git a/packages/component-faraday-ui/src/gridItems/Item.js b/packages/component-faraday-ui/src/gridItems/Item.js index 500bd409f5bb28fbd6957bc1ed1669cb4cae14e4..780a1125f49da355bb7f0b988be7a0d8c04046c3 100644 --- a/packages/component-faraday-ui/src/gridItems/Item.js +++ b/packages/component-faraday-ui/src/gridItems/Item.js @@ -2,6 +2,7 @@ import { isNumber } from 'lodash' import styled from 'styled-components' import { th } from '@pubsweet/ui-toolkit' +/** @component */ export default styled.div.attrs({ 'data-test-id': props => props['data-test-id'] || 'item', })` diff --git a/packages/component-faraday-ui/src/gridItems/Row.js b/packages/component-faraday-ui/src/gridItems/Row.js index 60fedfa89f628da9227c5021934e026dd89c35cc..06ab4eff0c9810a5f09356ea405a95bf047bf4cf 100644 --- a/packages/component-faraday-ui/src/gridItems/Row.js +++ b/packages/component-faraday-ui/src/gridItems/Row.js @@ -3,6 +3,7 @@ import styled from 'styled-components' import { marginHelper } from '../styledHelpers' +/** @component */ export default styled.div.attrs({ 'data-test-id': props => props['data-test-id'] || 'row', })` diff --git a/packages/component-faraday-ui/src/index.js b/packages/component-faraday-ui/src/index.js index 22efa382a9483bf47c55f2faf1ff0df4e2a3c7a7..0dd450ad6f3c3c6dae64914edfcc16120bc69644 100644 --- a/packages/component-faraday-ui/src/index.js +++ b/packages/component-faraday-ui/src/index.js @@ -13,6 +13,7 @@ export { default as Label } from './Label' export { default as Logo } from './Logo' export { default as ManuscriptCard } from './ManuscriptCard' export { default as PersonInfo } from './PersonInfo' +export { default as SortableList } from './SortableList' export { default as Tag } from './Tag' export { default as Text } from './Text' diff --git a/packages/component-faraday-ui/src/styledHelpers.js b/packages/component-faraday-ui/src/styledHelpers.js index 7e64825c5bab8cd2e6882045e8765e0780d1d5eb..2bdd97529c230d6bb12f4cb44c37020cd3df14d3 100644 --- a/packages/component-faraday-ui/src/styledHelpers.js +++ b/packages/component-faraday-ui/src/styledHelpers.js @@ -60,3 +60,25 @@ export const positionHelper = css` right: ${props => has(props, 'right') ? `${get(props, 'right')}px` : 'unset'}; ` + +export const radiusHelpers = props => { + const borderTop = props.isFirst + ? css` + border-top-left-radius: ${th('borderRadius')}; + border-top-right-radius: ${th('borderRadius')}; + ` + : css`` + + const borderBottom = props.isLast + ? css` + border-bottom-left-radius: ${th('borderRadius')}; + border-bottom-right-radius: ${th('borderRadius')}; + ` + : css`` + + return css` + border-radius: none; + ${borderTop}; + ${borderBottom}; + ` +} diff --git a/packages/components-faraday/src/components/Files/FileSection.js b/packages/components-faraday/src/components/Files/FileSection.js index 5811b4207a1b9c2a7e60afb0ca70035589740da4..148c51a92a5b52d186eff5450ad08ee6fd80377b 100644 --- a/packages/components-faraday/src/components/Files/FileSection.js +++ b/packages/components-faraday/src/components/Files/FileSection.js @@ -132,11 +132,11 @@ export default compose( { drop( { - changeList, - listId: toListId, - maxFiles, files, + maxFiles, setError, + changeList, + listId: toListId, allowedFileExtensions, }, monitor, @@ -176,11 +176,11 @@ export default compose( drop( { files, - maxFiles, addFile, - allowedFileExtensions, + maxFiles, setError, requestPending, + allowedFileExtensions, }, monitor, ) { @@ -203,9 +203,9 @@ export default compose( }, }, (connect, monitor) => ({ - connectFileDrop: connect.dropTarget(), isFileOver: monitor.isOver(), canDropFile: monitor.canDrop(), + connectFileDrop: connect.dropTarget(), }), ), )(FileSection) diff --git a/packages/styleguide/src/Wrapper.js b/packages/styleguide/src/Wrapper.js index 7b3bc02dddfe124353e8629bdd8bdaec5830d11a..b4b841f149478b8a9f38488cf845a48881effd7e 100644 --- a/packages/styleguide/src/Wrapper.js +++ b/packages/styleguide/src/Wrapper.js @@ -12,7 +12,7 @@ const store = createStore( }), ) -export default class ThemeWrapper extends Component { +export default class Wrapper extends Component { render() { return ( <Provider store={store}>