리액트 드래그앤드랍 구현기
react-beautiful-dnd
를 사용하여 구현하였다.
작업 메모
🏷 드래그 영역 변경하기
{...provided.dragHandleProps}ㅍ
를 드래그 적용 시키고 싶은 엘리먼트에 넣어준다.
<Draggable draggableId="draggable-1" index={0}>
{(provided) => (
<div ref={provided.innerRef} {...provided.draggableProps}>
<div {...provided.dragHandleProps}>DRAG AREA HERE</div>
<li>item</li>
</div>
)}
</Draggable>
실패작
`{/* <DragDrop setCards={setDragDropList}> {dragDropList.map((item, idx) => ( <DragDrop.Item key={idx} {...item} onClick={changeEdit} /> ))} </DragDrop> */}`
```
```
import { View, Text } from '@pip/theme';
```
import React from 'react';
import styled from 'styled-components';
import type { Identifier, XYCoord } from 'dnd-core';
import { ReactComponent as OrderSvg } from '@register/assets/icons/order.svg';
import { ReactComponent as EditSvg } from '@register/assets/icons/edit.svg';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { DndProvider, useDrag, useDrop } from 'react-dnd';
import update from 'immutability-helper';
export const ItemTypes = {
CARD: 'card',
};
const style = {
border: '1px dashed gray',
padding: '0.5rem 1rem',
marginBottom: '.5rem',
backgroundColor: 'white',
cursor: 'move',
};
interface DragDropProps {
id?: string;
title?: string;
idx?: number;
children?: React.ReactNode;
setCards?: any;
}
interface DragDropItemProps {
type?: string;
id?: any;
title: string;
index?: number;
onClick?:()=>void;
moveCard?: (dragIndex: number, hoverIndex: number) => void;
}
interface DragDrop extends React.FC {
Item: React.FC;
}
const DragDropItem: React.FC = ({ id, index, title, moveCard, onClick }) => {
const ref = React.useRef(null);
const \[{ handlerId }, drop\] = useDrop<DragDropItemProps, void, { handlerId: Identifier | null }>({
accept: ItemTypes.CARD,
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
};
},
hover(item: DragDropItemProps, monitor) {
if (!ref.current) {
return;
}
const dragIndex = item.index!;
const hoverIndex = index!;
```
if (dragIndex === hoverIndex) {
return;
}
const hoverBoundingRect = ref.current?.getBoundingClientRect();
const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
const clientOffset = monitor.getClientOffset();
const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top;
if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
return;
}
if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
return;
}
moveCard!(dragIndex, hoverIndex);
item.index = hoverIndex;
},
```
});
const \[{ isDragging }, drag\] = useDrag({
type: ItemTypes.CARD,
item: () => {
return { id, index };
},
collect: (monitor: any) => ({
isDragging: monitor.isDragging(),
}),
});
const opacity = isDragging ? 0 : 1;
drag(drop(ref));
return (
<StyledItem ref={ref} style={{ ...style, opacity }} onClick={onClick} data-handler-id={handlerId}>
{title}
);
};
const DragDrop: DragDrop = ({ setCards, children, ...props }) => {
const moveCard = React.useCallback((dragIndex: number, hoverIndex: number) => {
setCards((prevCards: any\[\]) =>
update(prevCards, {
$splice: \[
\[dragIndex, 1\],
\[hoverIndex, 0, prevCards\[dragIndex\] as any\],
\],
})
);
}, \[\]);
const childrens = React.Children.map(children, (child, index): any => {
if (React.isValidElement(child)) {
const key = child.key;
const id = child.props.id;
const title = child.props.title;
const option = { key, id, title, index, moveCard: moveCard };
return React.cloneElement(child as any, { ...option }, child.props.children);
}
});
return (
{childrens}
);
};
DragDrop.Item = DragDropItem;
export default DragDrop;
const StyledDragDrop = styled.div\`
width: 100%;
display: flex;
flex-direction: column;
gap: 8;
.dnd-card {
padding: 12px 16px 12px 21px;
& > div {
align-items: flex-start !important;
gap: 8px !important;
}
```
.card-title {
text-align: left !important;
}
```
}
\`;
const StyledItem = styled.div\`
display: flex;
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
flex: 1;
gap: 8px;
padding: 12px 15px 12px 21px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0px 2px 6px rgba(212, 223, 255, 0.5);
& + div {
margin-top: 8px;
}
.icon-order {
padding-top: 5px;
}
\`;
```
```