Last active
February 16, 2022 20:47
-
-
Save krzysu/1b391ae0e3c87d0c654cdf6d5a409632 to your computer and use it in GitHub Desktop.
how to integrate react-select with react-dnd
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component } from 'react'; | |
import Select from 'react-select' | |
import SortableItem from './SortableItem'; | |
import SortableContainer from './SortableContainer'; | |
import update from 'react/lib/update'; | |
const items = [ | |
{ value: '1', label: 'One' }, | |
{ value: '2', label: 'Two' }, | |
{ value: '3', label: 'Three' }, | |
{ value: '4', label: 'Four' }, | |
{ value: '5', label: 'Five' }, | |
{ value: '6', label: 'Six' }, | |
{ value: '7', label: 'Seven' }, | |
{ value: '8', label: 'Eight' }, | |
{ value: '9', label: 'Nine' }, | |
]; | |
class App extends Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
selected: ['2','4','5','6'], | |
items: items, | |
}; | |
this.onChange = this.onChange.bind(this); | |
this.valueRenderer = this.valueRenderer.bind(this); | |
this.swapItems = this.swapItems.bind(this); | |
} | |
onChange(allSelected) { | |
this.setState({ | |
selected: allSelected.map(item => item.value), | |
}); | |
} | |
valueRenderer(option, index) { | |
return ( | |
<SortableItem | |
index={index} | |
className="sortable-item" | |
swapItems={this.swapItems} | |
> | |
<span>{option.label}</span> | |
</SortableItem> | |
); | |
} | |
swapItems(dragIndex, hoverIndex) { | |
const dragItem = this.state.selected[dragIndex]; | |
this.setState(update(this.state, { | |
selected: { | |
$splice: [ | |
[dragIndex, 1], | |
[hoverIndex, 0, dragItem] | |
] | |
} | |
})); | |
} | |
render() { | |
return ( | |
<div className="select-wrap"> | |
<SortableContainer> | |
<Select | |
options={items} | |
value={this.state.selected.join(',')} | |
multi={true} | |
onChange={this.onChange} | |
valueRenderer={this.valueRenderer} | |
/> | |
</SortableContainer> | |
</div> | |
); | |
} | |
} | |
export default App; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component, PropTypes } from 'react'; | |
import { DragDropContext } from 'react-dnd'; | |
import HTML5Backend from 'react-dnd-html5-backend'; | |
class SortableContainer extends Component { | |
render() { | |
return <span>{this.props.children}</span>; | |
} | |
} | |
SortableContainer.propTypes = { | |
children: PropTypes.oneOfType([PropTypes.element, PropTypes.array]), | |
}; | |
export default DragDropContext(HTML5Backend)(SortableContainer); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React, { Component, PropTypes } from 'react'; | |
import { findDOMNode } from 'react-dom'; | |
import { DragSource, DropTarget } from 'react-dnd'; | |
const ITEM_TYPE = 'sortable'; | |
class SortableItem extends Component { | |
onMouseDown(event) { | |
event.stopPropagation(); // important! as react-select preventsDefault on mouseDown event, preventing also dragging | |
} | |
render() { | |
const { isDragging, connectDragSource, connectDropTarget, className, children } = this.props; | |
const opacity = isDragging ? 0 : 1; | |
return connectDropTarget(connectDragSource( | |
<span | |
className={className} | |
style={{ opacity }} | |
onMouseDown={this.onMouseDown} | |
> | |
{children} | |
</span> | |
)); | |
} | |
} | |
SortableItem.propTypes = { | |
// props from react-dnd | |
connectDragSource: PropTypes.func.isRequired, | |
connectDropTarget: PropTypes.func.isRequired, | |
isDragging: PropTypes.bool.isRequired, | |
// props provided by parent | |
index: PropTypes.number.isRequired, | |
children: PropTypes.element.isRequired, | |
swapItems: PropTypes.func.isRequired, | |
className: PropTypes.string, | |
}; | |
const source = { | |
beginDrag(props) { | |
return { | |
index: props.index, | |
}; | |
}, | |
}; | |
const target = { | |
hover(props, monitor, component) { | |
const dragIndex = monitor.getItem().index; | |
const hoverIndex = props.index; | |
// implement your own behaviour, below example taken from http://gaearon.github.io/react-dnd/examples-sortable-simple.html | |
if (dragIndex === hoverIndex) { | |
return; | |
} | |
const hoverBoundingRect = findDOMNode(component).getBoundingClientRect(); | |
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; | |
} | |
// when you want to swap items run | |
props.swapItems(dragIndex, hoverIndex); | |
// note: we're mutating the monitor item here! | |
monitor.getItem().index = hoverIndex; | |
}, | |
}; | |
function mapDropConnectToProps(connect) { | |
return { | |
connectDropTarget: connect.dropTarget(), | |
}; | |
} | |
function mapDragConnectToProps(connect, monitor) { | |
return { | |
connectDragSource: connect.dragSource(), | |
isDragging: monitor.isDragging(), | |
}; | |
} | |
export default DropTarget(ITEM_TYPE, target, mapDropConnectToProps)(DragSource(ITEM_TYPE, source, mapDragConnectToProps)(SortableItem)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment