Last active
November 12, 2019 17:36
-
-
Save crazypixel/add2ef919818c42e437f8799dd9e354e to your computer and use it in GitHub Desktop.
Drag & Drop with hooks - sortable list example
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, {useState, useCallback} from 'react'; | |
import styled from 'styled-components'; | |
import {range, inRange} from 'lodash'; | |
import Draggable from './Draggable'; | |
const MAX = 5; | |
const HEIGHT = 80; | |
const App = () => { | |
const items = range(MAX); | |
const [state, setState] = useState({ | |
order: items, | |
dragOrder: items, // items order while dragging | |
draggedIndex: null | |
}); | |
const handleDrag = useCallback(({translation, id}) => { | |
const delta = Math.round(translation.y / HEIGHT); | |
const index = state.order.indexOf(id); | |
const dragOrder = state.order.filter(index => index !== id); | |
if (!inRange(index + delta, 0, items.length)) { | |
return; | |
} | |
dragOrder.splice(index + delta, 0, id); | |
setState(state => ({ | |
...state, | |
draggedIndex: id, | |
dragOrder | |
})); | |
}, [state.order, items.length]); | |
const handleDragEnd = useCallback(() => { | |
setState(state => ({ | |
...state, | |
order: state.dragOrder, | |
draggedIndex: null | |
})); | |
}, []); | |
return ( | |
<Container> | |
{items.map(index => { | |
const isDragging = state.draggedIndex === index; | |
const top = state.dragOrder.indexOf(index) * (HEIGHT + 10); | |
const draggedTop = state.order.indexOf(index) * (HEIGHT + 10); | |
return ( | |
<Draggable | |
key={index} | |
id={index} | |
onDrag={handleDrag} | |
onDragEnd={handleDragEnd} | |
> | |
<Rect | |
isDragging={isDragging} | |
top={isDragging ? draggedTop : top} | |
> | |
{index} | |
</Rect> | |
</Draggable> | |
); | |
})} | |
</Container> | |
); | |
}; | |
export default App; | |
const Container = styled.div` | |
width: 100vw; | |
min-height: 100vh; | |
`; | |
const Rect = styled.div.attrs(props => ({ | |
style: { | |
transition: props.isDragging ? 'none' : 'all 500ms' | |
} | |
}))` | |
width: 300px; | |
user-select: none; | |
height: ${HEIGHT}px; | |
background: #fff; | |
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.15); | |
display: flex; | |
align-items: center; | |
justify-content: center; | |
position: absolute; | |
top: ${({top}) => 100 + top}px; | |
left: calc(50vw - 150px); | |
font-size: 20px; | |
color: #777; | |
`; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hello, and thank you for these examples. When trying this one, I've found that there is an error in handleDrag as the translation argument is undefined in the useCallback({translation, id}). Any insight on resolving this issue? Cheers!