Skip to content

Instantly share code, notes, and snippets.

@krzysu
Last active February 16, 2022 20:47
Show Gist options
  • Save krzysu/1b391ae0e3c87d0c654cdf6d5a409632 to your computer and use it in GitHub Desktop.
Save krzysu/1b391ae0e3c87d0c654cdf6d5a409632 to your computer and use it in GitHub Desktop.
how to integrate react-select with react-dnd
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;
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);
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