본문 바로가기
카테고리 없음

리액트 드래그앤드랍 구현기

by 코딩희송 2022. 9. 20.

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;  
}  
\`;

```
    ```

댓글