npx create-react-app myapp --template @thomd/cra-template-simple
cd myapp
npm install @hello-pangea/dnd --sav
Then update App.jsx
and npm start
import React, { useState } from 'react' | |
import { DragDropContext, Droppable, Draggable } from '@hello-pangea/dnd' | |
// fake data generator | |
const getItems = (count, offset = 0) => | |
Array.from({ length: count }, (v, k) => k).map((k) => ({ | |
id: `item-${k + offset}-${new Date().getTime()}`, | |
content: `item ${k + offset}`, | |
})) | |
const reorder = (list, startIndex, endIndex) => { | |
const result = Array.from(list) | |
const [removed] = result.splice(startIndex, 1) | |
result.splice(endIndex, 0, removed) | |
return result | |
} | |
// move item from one list to another list. | |
const move = (source, destination, droppableSource, droppableDestination) => { | |
const sourceClone = Array.from(source) | |
const destClone = Array.from(destination) | |
const [removed] = sourceClone.splice(droppableSource.index, 1) | |
destClone.splice(droppableDestination.index, 0, removed) | |
const result = {} | |
result[droppableSource.droppableId] = sourceClone | |
result[droppableDestination.droppableId] = destClone | |
return result | |
} | |
const getItemStyle = (isDragging, draggableStyle) => ({ | |
userSelect: 'none', | |
padding: '1rem', | |
margin: `0 0 1rem 0`, | |
background: isDragging ? 'lightgreen' : 'grey', | |
...draggableStyle, | |
}) | |
const getListStyle = (isDraggingOver) => ({ | |
background: isDraggingOver ? 'lightblue' : 'lightgrey', | |
padding: '1rem', | |
width: '16rem', | |
}) | |
export default function App() { | |
const [state, setState] = useState([getItems(4), getItems(4, 4)]) | |
function onDragEnd(result) { | |
const { source, destination } = result | |
if (!destination) { | |
return | |
} | |
const sInd = +source.droppableId | |
const dInd = +destination.droppableId | |
if (sInd === dInd) { | |
const items = reorder(state[sInd], source.index, destination.index) | |
const newState = [...state] | |
newState[sInd] = items | |
setState(newState) | |
} else { | |
const result = move(state[sInd], state[dInd], source, destination) | |
const newState = [...state] | |
newState[sInd] = result[sInd] | |
newState[dInd] = result[dInd] | |
setState(newState.filter((group) => group.length)) | |
} | |
} | |
return ( | |
<div> | |
<div style={{ display: 'flex' }}> | |
<DragDropContext onDragEnd={onDragEnd}> | |
{state.map((el, ind) => ( | |
<Droppable key={ind} droppableId={`${ind}`}> | |
{(provided, snapshot) => ( | |
<div ref={provided.innerRef} style={getListStyle(snapshot.isDraggingOver)} {...provided.droppableProps}> | |
{el.map((item, index) => ( | |
<Draggable key={item.id} draggableId={item.id} index={index}> | |
{(provided, snapshot) => ( | |
<div | |
ref={provided.innerRef} | |
{...provided.draggableProps} | |
{...provided.dragHandleProps} | |
style={getItemStyle(snapshot.isDragging, provided.draggableProps.style)}> | |
<div | |
style={{ | |
display: 'flex', | |
justifyContent: 'space-around', | |
}}> | |
{item.content} | |
</div> | |
</div> | |
)} | |
</Draggable> | |
))} | |
{provided.placeholder} | |
</div> | |
)} | |
</Droppable> | |
))} | |
</DragDropContext> | |
</div> | |
</div> | |
) | |
} |