-
Notifications
You must be signed in to change notification settings - Fork 2
/
drag-drop.tsx
94 lines (84 loc) · 2.71 KB
/
drag-drop.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import React from "react";
import { RootContainer, SEOContainer } from "~containers";
import { Main, Hero, Card } from "~components";
export default function DragAndDropPage() {
const [items, setItems] = React.useState([
"🍎 Apple",
"🍞 Bread",
"🍰 Cake",
"🍩 Donut",
"🥚 Egg",
"🍟 Fries",
"🍇 Grapes",
"🍯 Honey",
"🍦 Icecream",
"🌶 Jalapeño",
"🥝 Kiwi",
"🍋 Lemon",
]);
const draggedIdx = React.useRef(-1);
const handleDragStart = (event: React.DragEvent<HTMLDivElement>, idx: number) => {
const element = event.target as HTMLLIElement;
event.dataTransfer.effectAllowed = "move";
// @ts-expect-error setData expects the second argument to be string
event.dataTransfer.setData("text/html", element);
draggedIdx.current = idx;
window.requestAnimationFrame(() => {
element.style.opacity = "0"; // needed to load full-opacity element into drag image
});
};
const handleDragEnter = (idx: number) => {
setItems(
items.map((item, i) => {
if (i === draggedIdx.current) return items[idx];
if (i === idx) return items[draggedIdx.current];
return item;
})
);
draggedIdx.current = idx;
};
const handleDragOver: React.DragEventHandler = (event) => {
event.preventDefault(); // needed to prevent animation back to original location
};
const handleDragEnd: React.DragEventHandler<HTMLDivElement> = (event) => {
(event.target as HTMLLIElement).style.opacity = "1";
draggedIdx.current = -1;
};
return (
<RootContainer>
<Main>
<Hero
title="A simple drag-and-drop doesn't require any additional dependencies in React, everything is possible through the native API"
codeLink="https://github.com/mrozilla/mrozilla.cz/blob/master/src/pages/lab/drag-drop.tsx"
/>
<section style={{ maxWidth: "400px" }}>
<Card style={{ gap: "1rem" }}>
{items.map((item, i) => (
<Card
key={item}
tag="li"
draggable
onDragStart={(e) => handleDragStart(e, i)}
onDragEnter={() => handleDragEnter(i)}
onDragOver={handleDragOver}
onDragEnd={handleDragEnd}
>
{item}
</Card>
))}
</Card>
</section>
</Main>
</RootContainer>
);
}
export const Head = () => {
return (
<SEOContainer
title="Drag'n'drop"
description="A simple drag-and-drop doesn't require any additional dependencies in React, everything is possible through the native API"
permalink="/lab/drag-drop"
ogImage="/assets/og.png"
/>
);
};