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

feat(styleguide): add react dnd to filesection - WIP

parent 4524ea61
No related branches found
No related tags found
1 merge request!43Sprint #19
Showing
with 289 additions and 61 deletions
...@@ -31,7 +31,7 @@ const hasPreview = (name = '') => { ...@@ -31,7 +31,7 @@ const hasPreview = (name = '') => {
} }
const FileItem = ({ const FileItem = ({
file, item: file,
fileSize, fileSize,
onPreview, onPreview,
removeFile, removeFile,
...@@ -69,7 +69,7 @@ const FileItem = ({ ...@@ -69,7 +69,7 @@ const FileItem = ({
FileItem.propTypes = { FileItem.propTypes = {
/** The file. */ /** The file. */
file: PropTypes.shape({ item: PropTypes.shape({
id: PropTypes.string, id: PropTypes.string,
name: PropTypes.string, name: PropTypes.string,
size: PropTypes.number, size: PropTypes.number,
...@@ -83,7 +83,7 @@ FileItem.propTypes = { ...@@ -83,7 +83,7 @@ FileItem.propTypes = {
} }
export default compose( export default compose(
withProps(({ file: { name, size } }) => ({ withProps(({ name, size }) => ({
hasPreview: hasPreview(name), hasPreview: hasPreview(name),
fileSize: parseFileSize(size), fileSize: parseFileSize(size),
})), })),
......
...@@ -2,7 +2,7 @@ A pdf file. ...@@ -2,7 +2,7 @@ A pdf file.
```js ```js
<FileItem <FileItem
file={{ item={{
id: 'file1', id: 'file1',
name: 'myfile.pdf', name: 'myfile.pdf',
size: 1231312, size: 1231312,
...@@ -16,7 +16,7 @@ A Word document (no preview available). ...@@ -16,7 +16,7 @@ A Word document (no preview available).
```js ```js
<FileItem <FileItem
file={{ item={{
id: 'file1', id: 'file1',
name: 'myfile.docx', name: 'myfile.docx',
size: 51312, size: 51312,
...@@ -30,7 +30,7 @@ With a drag handle. ...@@ -30,7 +30,7 @@ With a drag handle.
```js ```js
<FileItem <FileItem
file={{ item={{
id: 'file1', id: 'file1',
name: 'myfile.pdf', name: 'myfile.pdf',
size: 1231312, size: 1231312,
......
import React from 'react' import React from 'react'
import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit' import { th } from '@pubsweet/ui-toolkit'
import { FilePicker } from '@pubsweet/ui' import { FilePicker } from '@pubsweet/ui'
import styled, { css } from 'styled-components'
import { compose, withProps } from 'recompose' 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 = { const EXTENSIONS = {
pdf: 'PDF', pdf: 'PDF',
...@@ -18,6 +28,7 @@ const FileSection = ({ ...@@ -18,6 +28,7 @@ const FileSection = ({
isLast, isLast,
isFirst, isFirst,
required, required,
files = [],
supportedFormats, supportedFormats,
allowedFileExtensions, allowedFileExtensions,
}) => ( }) => (
...@@ -27,7 +38,7 @@ const FileSection = ({ ...@@ -27,7 +38,7 @@ const FileSection = ({
<Label required={required}>{title}</Label> <Label required={required}>{title}</Label>
<FilePicker <FilePicker
allowedFileExtensions={allowedFileExtensions} allowedFileExtensions={allowedFileExtensions}
onUpload={file => console.log('uploaded ', file)} onUpload={file => {}}
> >
<ActionLink icon="plus" size="small"> <ActionLink icon="plus" size="small">
UPLOAD FILE UPLOAD FILE
...@@ -42,22 +53,10 @@ const FileSection = ({ ...@@ -42,22 +53,10 @@ const FileSection = ({
</Item> </Item>
)} )}
</Row> </Row>
<FileItem <SortableList
dragHandle={DragHandle} dragHandle={DragHandle}
file={{ items={files}
id: 'file1', listItem={FileItem}
name: 'myfile.docx',
size: 51312,
}}
mb={1}
/>
<FileItem
dragHandle={DragHandle}
file={{
id: 'file1',
name: 'myfile.docx',
size: 51312,
}}
mb={1} mb={1}
/> />
</Root> </Root>
...@@ -72,28 +71,6 @@ export default compose( ...@@ -72,28 +71,6 @@ export default compose(
)(FileSection) )(FileSection)
// #region styles // #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` const Root = styled.div`
background: ${th('colorBackground')}; background: ${th('colorBackground')};
min-height: calc(${th('gridUnit')} * 22); min-height: calc(${th('gridUnit')} * 22);
......
A section that shows FileItems. Drag and drop support. A section that shows FileItems. Drag and drop support.
```js ```js
const files = [
{
id: 'file1',
name: 'myfile.docx',
size: 51312,
},
{
id: 'file2',
name: 'myfile.docx',
size: 133127,
},
];
<FileSection <FileSection
files={files}
title="Main Manuscript" title="Main Manuscript"
required required
allowedFileExtensions={['pdf', 'doc', 'docx']} allowedFileExtensions={['pdf', 'doc', 'docx']}
...@@ -11,16 +25,31 @@ A section that shows FileItems. Drag and drop support. ...@@ -11,16 +25,31 @@ A section that shows FileItems. Drag and drop support.
Multiple sections on top of each other. Multiple sections on top of each other.
```js ```js
const files = [
{
id: 'file1',
name: 'myfile.docx',
size: 51312,
},
{
id: 'file2',
name: 'myfile.docx',
size: 133127,
},
];
<div style={{ display: 'flex', flexDirection: 'column' }}> <div style={{ display: 'flex', flexDirection: 'column' }}>
<FileSection <FileSection
title="Main Manuscript"
isFirst isFirst
required required
files={files}
title="Main Manuscript"
allowedFileExtensions={['pdf', 'doc', 'docx']} allowedFileExtensions={['pdf', 'doc', 'docx']}
/> />
<FileSection <FileSection
title="Cover Letter"
required required
title="Cover Letter"
files={files}
allowedFileExtensions={['pdf', 'doc', 'docx']} allowedFileExtensions={['pdf', 'doc', 'docx']}
/> />
<FileSection title="Supplimental Files" required isLast /> <FileSection title="Supplimental Files" required isLast />
......
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 export default SortableList
A list with drag and drop support. A list with drag and drop support.
```js ```js
<SortableList /> const items = [
``` { firstName: 'John', lastName: 'Doe' },
\ No newline at end of file { 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 />
```
import styled from 'styled-components' import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit' import { th } from '@pubsweet/ui-toolkit'
// #region styles /** @component */
export default styled.div` export default styled.div`
background-color: ${props => background-color: ${props =>
props.status ? th('tag.statusBackgroundColor') : th('tag.backgroundColor')}; props.status ? th('tag.statusBackgroundColor') : th('tag.backgroundColor')};
...@@ -15,4 +15,3 @@ export default styled.div` ...@@ -15,4 +15,3 @@ export default styled.div`
text-transform: uppercase; text-transform: uppercase;
width: fit-content; width: fit-content;
` `
// #endregion
...@@ -36,6 +36,7 @@ const fontSize = css` ...@@ -36,6 +36,7 @@ const fontSize = css`
props.small ? th('lineHeightBaseSmall') : th('lineHeightBase')}; props.small ? th('lineHeightBaseSmall') : th('lineHeightBase')};
` `
/** @component */
export default styled.span` export default styled.span`
display: inline-block; display: inline-block;
font-style: ${props => props.fontStyle || 'normal'}; font-style: ${props => props.fontStyle || 'normal'};
......
...@@ -2,6 +2,7 @@ import { isNumber } from 'lodash' ...@@ -2,6 +2,7 @@ import { isNumber } from 'lodash'
import styled from 'styled-components' import styled from 'styled-components'
import { th } from '@pubsweet/ui-toolkit' import { th } from '@pubsweet/ui-toolkit'
/** @component */
export default styled.div.attrs({ export default styled.div.attrs({
'data-test-id': props => props['data-test-id'] || 'item', 'data-test-id': props => props['data-test-id'] || 'item',
})` })`
......
...@@ -3,6 +3,7 @@ import styled from 'styled-components' ...@@ -3,6 +3,7 @@ import styled from 'styled-components'
import { marginHelper } from '../styledHelpers' import { marginHelper } from '../styledHelpers'
/** @component */
export default styled.div.attrs({ export default styled.div.attrs({
'data-test-id': props => props['data-test-id'] || 'row', 'data-test-id': props => props['data-test-id'] || 'row',
})` })`
......
...@@ -13,6 +13,7 @@ export { default as Label } from './Label' ...@@ -13,6 +13,7 @@ export { default as Label } from './Label'
export { default as Logo } from './Logo' export { default as Logo } from './Logo'
export { default as ManuscriptCard } from './ManuscriptCard' export { default as ManuscriptCard } from './ManuscriptCard'
export { default as PersonInfo } from './PersonInfo' export { default as PersonInfo } from './PersonInfo'
export { default as SortableList } from './SortableList'
export { default as Tag } from './Tag' export { default as Tag } from './Tag'
export { default as Text } from './Text' export { default as Text } from './Text'
......
...@@ -60,3 +60,25 @@ export const positionHelper = css` ...@@ -60,3 +60,25 @@ export const positionHelper = css`
right: ${props => right: ${props =>
has(props, 'right') ? `${get(props, 'right')}px` : 'unset'}; 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};
`
}
...@@ -132,11 +132,11 @@ export default compose( ...@@ -132,11 +132,11 @@ export default compose(
{ {
drop( drop(
{ {
changeList,
listId: toListId,
maxFiles,
files, files,
maxFiles,
setError, setError,
changeList,
listId: toListId,
allowedFileExtensions, allowedFileExtensions,
}, },
monitor, monitor,
...@@ -176,11 +176,11 @@ export default compose( ...@@ -176,11 +176,11 @@ export default compose(
drop( drop(
{ {
files, files,
maxFiles,
addFile, addFile,
allowedFileExtensions, maxFiles,
setError, setError,
requestPending, requestPending,
allowedFileExtensions,
}, },
monitor, monitor,
) { ) {
...@@ -203,9 +203,9 @@ export default compose( ...@@ -203,9 +203,9 @@ export default compose(
}, },
}, },
(connect, monitor) => ({ (connect, monitor) => ({
connectFileDrop: connect.dropTarget(),
isFileOver: monitor.isOver(), isFileOver: monitor.isOver(),
canDropFile: monitor.canDrop(), canDropFile: monitor.canDrop(),
connectFileDrop: connect.dropTarget(),
}), }),
), ),
)(FileSection) )(FileSection)
......
...@@ -12,7 +12,7 @@ const store = createStore( ...@@ -12,7 +12,7 @@ const store = createStore(
}), }),
) )
export default class ThemeWrapper extends Component { export default class Wrapper extends Component {
render() { render() {
return ( return (
<Provider store={store}> <Provider store={store}>
......
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