Skip to content

Instantly share code, notes, and snippets.

@jlroettger
Created August 30, 2016 00:53
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save jlroettger/2d6d7ae572f985fa176c27a63cadf292 to your computer and use it in GitHub Desktop.
Save jlroettger/2d6d7ae572f985fa176c27a63cadf292 to your computer and use it in GitHub Desktop.
React DND and Material UI - Reorderable List
export ReorderableList from './ReorderableList'
export ReorderableListItem from './ReorderableListItem'
import React, { Component } from 'react'
import { Link } from 'react-router'
// Drag and Drop
import { DragDropContext } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
// Material UI
import { List } from 'material-ui/List'
import Subheader from 'material-ui/Subheader'
class ReorderableList extends Component {
constructor(props) {
super(props)
this.state = {
dndConstraint: this.props.dndConstraint || (Math.random() + 1).toString()
}
}
render() {
const { children, ...props } = this.props
const { dndConstraint } = this.state
return (
<List { ...props }>
{React.Children.map(children, (child) => {
if (['div', Subheader, Link].includes(child.type)) return child
return React.cloneElement(child, { listIdentifier: dndConstraint })
})}
</List>
);
}
}
export default DragDropContext(HTML5Backend)(ReorderableList)
import React, { Component, PropTypes } from 'react'
// Drag and Drop
import { findDOMNode } from 'react-dom'
import { DragSource, DropTarget } from 'react-dnd'
// Material UI
import { ListItem } from 'material-ui/List'
import { fade } from 'material-ui/utils/colorManipulator'
const itemSource = {
beginDrag(props) {
let { id, index } = props
return {
id,
index
}
}
}
const itemTarget = {
hover(props, monitor, component) {
let { id: dragId, index: dragIndex } = monitor.getItem()
let { id: hoverId, index: hoverIndex } = props
if (dragIndex === hoverIndex) return; // Don't replace items with themselves
let hoveringOffsets = findDOMNode(component).getBoundingClientRect()
let penPercent = 0.50 // Percentage distance into next item before swap
let penMin = (hoveringOffsets.bottom - hoveringOffsets.top) * penPercent
let clientOffset = monitor.getClientOffset()
let penY
// Dragging downwards
if (dragIndex < hoverIndex) penY = clientOffset.y - hoveringOffsets.top
// Dragging upwards
if (dragIndex > hoverIndex) penY = hoveringOffsets.bottom - clientOffset.y
if ( !(penY > penMin) ) return // Exit it haven't penetrated enough
// Time to actually perform the action
props.moveItem({ dragId, dragIndex, hoverId, hoverIndex })
// Note: we're mutating the monitor item here!
// Generally it's better to avoid mutations,
// but it's good here for the sake of performance
// to avoid expensive index searches.
monitor.getItem().index = hoverIndex
}
}
const dndType = props => props.listIdentifier || 'reorderableListItem'
class ReorderableListItem extends Component {
static contextTypes = {
muiTheme: PropTypes.object.isRequired
}
componentDidMount() {
// Does not work in IE
const img = new Image();
img.onload = () => this.props.connectDragPreview(img);
// Single transparent pixel
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'
}
render() {
const textColor = this.context.muiTheme.baseTheme.palette.textColor;
const {
children,
connectDragSource,
connectDropTarget,
isDragging,
style,
// eslint-disable-next-line no-unused-vars
index, moveReport, renameReport, moveItem, connectDragPreview, // omit pass hackAutoComplete
...props
} = this.props
return connectDragSource(connectDropTarget(
<div>
<ListItem
{ ...props }
style={{
...style,
backgroundColor: isDragging && fade(textColor, 0.4),
cursor: isDragging && 'move'
}}
>
{children}
</ListItem>
</div>
))
}
}
const connectTarget = connect => ({
connectDropTarget: connect.dropTarget()
})
const connectSource = (connect, monitor) => ({
connectDragSource: connect.dragSource(),
connectDragPreview: connect.dragPreview(),
isDragging: monitor.isDragging()
})
export default (
DropTarget(dndType, itemTarget, connectTarget)(
DragSource(dndType, itemSource, connectSource)(
ReorderableListItem
)
)
)
@olenakozdoba
Copy link

Hi! is there an example on how to use it? I tried

<ReorderableList>
<ReorderableListItem primaryText="Item1" />
<ReorderableListItem primaryText="Item2" />
<ReorderableListItem primaryText="Item3" />
</ReordeableList>

and it didn't work.

Am I missing something?

@kavimaluskam
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment