magic.placeholder let the Droppable Board keep the original size.
onDrag는 드래그가 끝났을 때 실행될 함수를 arg로 받는다 & gives many information about what happend when the drag finished
import { DragDropContext, Draggable, Droppable } from "react-beautiful-dnd";
import styled from "styled-components";
const Wrapper = styled.div`
display: flex;
max-width: 480px;
width:100%;
margin: 0 auto;
justify-content: center;
align-items: center;
height:100vh;
`
const Boards = styled.div`
display: grid;
width:100%;
grid-template-columns: repeat(1, 1fr);
`
const Board = styled.div`
padding: 20px 10px;
padding-top:30px;
background-color: ${props => props.theme.boardColor};
border-radius: 5px;
min-height:200px;
`
const Card = styled.div`
background-color: ${props => props.theme.cardColor};
padding: 5px 10px;
margin-bottom:5px;
border-radius: 5px;
`;
const toDos = ["a", "b", "c", "d", "e", "f"];
function App() {
const onDragEnd = () => { }
return <DragDropContext onDragEnd={onDragEnd}>
<Wrapper>
<Boards>
<Droppable droppableId="one">
{(magic) =>
<Board ref={magic.innerRef} {...magic.droppableProps}>
{toDos.map((toDo, index) => <Draggable draggableId={toDo} index={index} key={index}>
{(magic) => (<Card
ref={magic.innerRef}
{...magic.draggableProps}{...magic.dragHandleProps}
>
{toDo}
</Card>)}
</Draggable>)}
{magic.placeholder}
</Board>}
</Droppable>
</Boards>
</Wrapper>
</DragDropContext >
}
export default App;
import { atom, selector } from "recoil";
export const toDoState = atom({
key: "toDo",
default: ["a", "b", "c", "d", "e", "f"],
})
todos( ["a", "b", "c", "d", "e", "f"])를 가지고 있는데 이것으로 두 단게를 거쳐 볼 것이다~.
1. source.array 로부터 index를 삭제할 것이다!
array.splice(a,b) => array의 a인덱스부터 b개 만큼 삭제한다.
array.splice(a, 0, c) => array의 a인덱스부터 0개(아무것도 지우지 않고)를 지우고, c를 삽입한다.
key & draggableId must be the same
const onDragEnd = ({ draggableId, destination, source }: DropResult) => {
if (!destination) return;
setToDos(oldToDos => {
const copyToDos = [...oldToDos];
// 1) Delete item on source.index
copyToDos.splice(source.index, 1)
// 2) Put back the item on the destination.index
copyToDos.splice(destination?.index, 0, draggableId)
return copyToDos;
})
}
Object.keys => 키들을 배열로 반환하여 준다.
!destination은 옮기지 않았을 때의 경우.
useRecoilState의 함수를 사용하여 바뀐 순서를 다시 저장한다.
이 때 사용할 속성들(draggableId, destination, source)은 DropResult에서 가져올 수 있다.
react memeo => allow react.js please not to rerender this component if the props didn't change
사용법
원래 export default DraggableCard => export default React.memo(DraggableCard)
interface 설정을 해주지 않으면 typescript가 저 세가지 유형만 허용하기때문에 다른 key도 차후에 들어올수 있게 하려면 저렇게 interface를 만들어 설정해 주어야 한다.
interface IToDoState { [key: string]: string[]; } export const toDoState = atom<IToDoState>({ key: "toDo", default: { to_do: ["a", "b",], doing: ["c", "d", "e"], done: ["f"], }, })
1) make a copy only of a array that happened modifying
2) then we put that copy next to the older things
const [toDos, setToDos] = useRecoilState(toDoState);
const onDragEnd = (info: DropResult) => {
console.log(info);
const { destination, draggableId, source } = info;
if (!destination) return;
if (destination?.droppableId === source.droppableId) {
//same board movement
setToDos((allBoards) => {
//allBoards is not array anymore
//copy only the board that changed
//when we return, must return boardCopy and previous states of other Boards
const boardCopy = [...allBoards[source.droppableId]];
boardCopy.splice(source.index, 1)
boardCopy.splice(destination?.index, 0, draggableId)
return {
...allBoards,
[source.droppableId]: boardCopy
};
})
}
if (destination.droppableId !== source.droppableId) {
// cross board movement
setToDos(allBoards => {
const sourceBoard = [...allBoards[source.droppableId]];
const destinationBoard = [...allBoards[destination.droppableId]];
sourceBoard.splice(source.index, 1);
destinationBoard.splice(destination?.index, 0, draggableId);
return {
...allBoards,
[source.droppableId]: sourceBoard,
[destination.droppableId]: destinationBoard
}
})
}
}
움직이기 편하게 하기위해서는 receiver의 크기 도 중요함
=> requires another argument(other tahn magic(which is provided)) : snapshot tells that if the user is draggingFrom(leave) or draggingOver(arrive)
interface IAreaProps {
isDraggingOver: boolean;
DraggingFromThisWith: boolean;
}
const Area = styled.div <IAreaProps> `
background-color: ${props => props.isDraggingOver ? "pink" : props.DraggingFromThisWith ? "red" : "blue"};
flex-grow:1;
height: 100%;
`
function Board({ toDos, boardId }: IBoardProps) {
return (
<Wrapper>
<Title>{boardId}</Title>
<Droppable droppableId={boardId}>
{(magic, snapshot) =>
<Area isDraggingOver={snapshot.isDraggingOver}
DraggingFromThisWith={Boolean(snapshot.draggingFromThisWith)} ref={magic.innerRef} {...magic.droppableProps}>
{toDos.map((toDo, index) => <DraggableCard key={toDo} toDo={toDo} index={index} />
)}
{magic.placeholder}
</Area>}
</Droppable>
</Wrapper>
)
}