<SortableList />

A List that can be sorted through drag and drop.

Component

1import React, { useRef } from "react";
2import "./Styles/_sortablelist.scss";
3
4export default function SortableList({ ...props }) {
5 const { children } = props;
6 const ref = useRef();
7
8 const dragOver = (e) => {
9 e.preventDefault();
10 const afterElement = getDragAfterElement(ref.current, e.clientY);
11 const draggable = ref.current.querySelector(".dragging");
12
13 if (afterElement === null) {
14 ref.current.appendChild(draggable);
15 } else {
16 ref.current.insertBefore(draggable, afterElement);
17 }
18 };
19
20 const getDragAfterElement = (container, y) => {
21 const draggableElements = Array.from(
22 container.querySelectorAll(".draggable:not(.dragging)")
23 );
24
25 return draggableElements.reduce(
26 (closest, child) => {
27 const box = child.getBoundingClientRect();
28 const offset = y - box.top - box.height / 2;
29
30 if (offset < 0 && offset > closest.offset) {
31 return { offset, element: child };
32 } else {
33 return closest;
34 }
35 },
36 { offset: Number.NEGATIVE_INFINITY }
37 ).element;
38 };
39
40 return (
41 <ul ref={ref} onDragOver={dragOver} className="Sortable-List">
42 {children}
43 </ul>
44 );
45}
46
47export { SortableListItem } from "./Components/SortableListItem";

Styles

1.Sortable-List {
2 width: 50%;
3 margin: auto;
4 border: 2px solid #e3e5e4;
5 padding: 0;
6 list-style: none;
7
8 & li {
9 display: flex;
10 background-color: white;
11
12 &:not(:last-of-type) {
13 border-bottom: 2px solid #e3e5e4;
14 }
15
16 &.dragging {
17 opacity: 0.5;
18 }
19
20 & > span {
21 background-color: #c3c7ca;
22 display: flex;
23 align-items: center;
24 justify-content: center;
25 font-size: 20px;
26 height: 60px;
27 width: 60px;
28 }
29
30 & > div {
31 cursor: pointer;
32 display: flex;
33 align-items: center;
34 justify-content: space-between;
35 padding: 15px;
36 flex: 1;
37 }
38 }
39}

Usage

1