[React] ๋ฆฌ์กํธ ์์ ๋๋๊ทธ์ค๋๋กญ ๊ตฌํ
TL;DR
- ๋๋๊ทธํ ์ ์๋ ์์๋ก ๋ณ๊ฒฝ —
draggable={true}
- ๋๋๊ทธ ์์ — onDragStart ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ
- ๋๋๊ทธํ๊ณ ์๋ ์์์ ์ธ๋ฑ์ค ์ ๋ณด ์ ์ฅ —
state.draggedFrom
- ๋๋๊ทธ ์ํ true๋ก ๋ณ๊ฒฝ —
state.isDragging
- ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ ์์ ์ ์๋ฆฌ๋จผํธ ๋ฆฌ์คํธ ์ ์ฅ —
state.originalOrder
- ๋๋๊ทธํ๊ณ ์๋ ์์์ ์ธ๋ฑ์ค ์ ๋ณด ์ ์ฅ —
- ๋ง์ฐ์ค ์ปค์๊ฐ ๋๋กญ ๊ฐ๋ฅํ ์์ญ์ ์์ ๋ — onDragOver ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ
- drop ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก dragOver ๊ธฐ๋ณธ ์ด๋ฒคํธ ๋ฐฉ์ง —
e.preventDefault()
- ๋ง์ฐ์ค ํฌ์ธํฐ ์์น์ ์๋ ์์์ ์ธ๋ฑ์ค ์ ์ฅ —
state.draggedTo
- ์๋ฆฌ๋จผํธ ์์ ๋ณ๊ฒฝ —
state.updatedOrder
๋๋๊ทธ์ค์ธ ์์ดํ ์ ๋ง์ฐ์ค ํฌ์ธํฐ ์์น(draggedTo ์ธ๋ฑ์ค)๋ก ์ด๋. ๊ธฐ์กด ๋ง์ฐ์ค ํฌ์ธํฐ ์์น์ ์๋ ์์๋ ๋ฐ๋ก ๋ค๋ก ๋ฐ๋ฆผ.
- drop ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก dragOver ๊ธฐ๋ณธ ์ด๋ฒคํธ ๋ฐฉ์ง —
- ๋๋กญ ๊ฐ๋ฅํ ์์ญ์์ ๋๋กญํ์ ๋ — onDrop ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ
- ์์๋ฅผ ๋ณ๊ฒฝํ ์๋ฆฌ๋จผํธ ๋ฆฌ์คํธ ๋ ๋
- ๋๋๊ทธ์ค๋๋กญ ๊ด๋ จ ์ํ ์ด๊ธฐํ
๋ฐฐ๊ฒฝ ์ง์
HTML ๋๋๊ทธ์ค๋๋กญ Web API๋ฅผ ์ด์ฉํด ๋ฆฌ์คํธ ์๋ฆฌ๋จผํธ์ ์์๋ฅผ ๋ง์ฐ์ค ๋๋๊ทธ์ค๋๋กญ์ผ๋ก ๋ฐ๊ฟ ์ ์๋ค. ์๋ฆฌ๋จผํธ์ draggable
์์ฑ์ true
๋ก ์ฃผ๋ฉด ํด๋น ์์๋ ๋๋๊ทธ ๊ฐ๋ฅํ ๊ฐ์ฒด๊ฐ ๋๋ค. ์ด๋ฏธ์ง, ๋งํฌ, ์ ํํ ํ
์คํธ(ํ
์คํธ ๋ธ๋ก)๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋๋๊ทธํ ์ ์๋ค. ๋๋๊ทธ ๊ฐ๋ฅ ์ํ๊ฐ ๋๋ฉด onDragStart
๊ฐ์ ๋๋๊ทธ ๊ด๋ จ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ ์ ์๋ค. onDragStart
๋ ๋๋๊ทธ๊ฐ ์์๋๋ฉด ํธ๋ฆฌ๊ฑฐ๋๋ ์ด๋ฒคํธ๋ค.
// ๋ฆฌ์คํธ ์์
<div draggable="true" onDragStart={startDragging}>
Drag Me ๐ฐ
</div>;
์๋ฆฌ๋จผํธ์ onDrop
๊ณผ onDragOver
์ด๋ฒคํธ๋ฅผ ๊ฑธ๋ฉด ๋๋กญ ๊ฐ๋ฅํ ์์ญ(์ ํจํ ๋๋กญ ๋์)์ด ๋๋ค. ์ด ๋ ์ด๋ฒคํธ๋ฅผ ํ์ฉํด ๋๋๊ทธ์ค๋๋กญ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์๋ค. onDragOver
๋ ๋ง์ฐ์ค ํฌ์ธํฐ์ ์๋ ์์๊ฐ ์ ํจํ ๋๋กญ ๋์์ผ ๋ ํธ๋ฆฌ๊ฑฐ ๋๋ฉฐ, onDrop
์ ๋๋กญ ๊ฐ๋ฅํ ์์ญ์์ ๋๋กญํ์ ๋ ํธ๋ฆฌ๊ฑฐ๋๋ ์ด๋ฒคํธ๋ค.
<section onDrop={updateDragAndDropState} onDragOver={receiveDraggedElements}>
Drop here ๐คฒ๐ป
</section>;
๋๋๊ทธํ (์์)๋ฐ์ดํฐ์ ์ํธ ์์ฉํ๊ธฐ ์ํด setData()
์ getData()
๊ฐ์ ์ด๋ฒคํธ ๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์๋ ์๋ค. ํ์ฌ ๋๋๊ทธ ํ๊ณ ์๋ ์์ ์ ๋ณด(id ๋ฑ)๋ฅผ setData()
๋ฅผ ํตํด ์ ์ฅํ๊ณ , ๋๋กญํ์ ๋ getData()
๋ฅผ ํตํด ์์ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์จ ํ ์ฌ์ ๋ ฌ ํ๋ ๋ฐฉ์์ผ๋ก ํ์ฉํ ์ ์๋ค.
event.dataTransfer.setData(key, value); // ์ฌ๋ฌ key๋ฅผ ์ด์ฉํด ๋ค์์ ๋ฐ์ดํฐ๋ฅผ ์ ์ฅํ ์ ์๋ค
event.dataTransfer.getData(key);
์ํ
๋ฆฌ์กํธ ์ปดํฌ๋ํธ ๋ฐ๊นฅ ์์ญ์ ๋ฆฌ์คํธ๋ฅผ ๋ ๋ํ ์ํ(items)์, ๋๋๊ทธ์ค๋๋กญ๊ณผ ๊ด๋ จํ ์ํ์ ์ด๊ธฐ๊ฐ์ ์ ์ํ๋ค. ๋๋๊ทธ์ค๋๋กญ ์ํ์ ๋๋๊ทธ ์ค์ธ ์์์ ์ธ๋ฑ์ค, ์ ๋ฐ์ดํธ ์ ํ์ ๋ ๋ ๋ฆฌ์คํธ(items), ๋๋๊ทธ ์ํ๋ฅผ ์ ์ฅํ๋ ๊ณณ์ด๋ค.
const items = [
{ number: "3", title: "๐จ๐ป๐ป Tech Man" },
// ์๋ต
];
const initialDnDState = {
draggedFrom: null, // ๋๋๊ทธ๋ฅผ ์์ํ ์์์(๋ง์ฐ์ค๋ฅผ ํด๋ฆญํ์ฌ ์์ง์ธ ์์) ์ธ๋ฑ์ค
draggedTo: null, // ๋๋กญ ๋์ ์์์ ์ธ๋ฑ์ค(๋๋๊ทธํ์ฌ ๋ง์ฐ์ค ์ปค์๊ฐ ์์นํ ์์์ ์ธ๋ฑ์ค)
isDragging: false, // ๋๋๊ทธ ์ฌ๋ถ Boolean
originalOrder: [], // ๋๋กญํ๊ธฐ์ (์์๊ฐ ๋ฐ๋๊ธฐ ์ ) ๊ธฐ์กด list
updatedOrder: [], // ๋๋กญํ ํ ์์๊ฐ ๋ฐ๋ list
};
useState
ํ
์ ์ด์ฉํด ๋ ๋ ๋ฆฌ์คํธ์ ๋๋๊ทธ์ค๋๋กญ ์ด๊ธฐ๊ฐ์ ์ปดํฌ๋ํธ ๋ด๋ถ ์ํ๋ก ๋๋ค.
const Lists = () => {
const [list, setList] = useState(items); // ๋ ๋๋ ์์
const [dragAndDrop, setDragAndDrop] = useState(initialDnDState); // D&D ๊ด๋ จ ์ํ
return (
<section>
<ul>
{list.map((item, index) => (
<li>...</li>
))}
</ul>
</section>
);
};
๋๋๊ทธ ๊ฐ๋ฅํ ์์๋ก ๋ณ๊ฒฝ
โ๋งํฌ โ์ด๋ฏธ์ง โ์ ํํ ํ ์คํธ(ํ ์คํธ ๋ธ๋ก)๋ ๊ธฐ๋ณธ๊ฐ์ด ๋๋๊ทธ ๊ฐ๋ฅ์ด๋ค(draggable="ture")
<li>
ํ๊ทธ์ draggable
์์ฑ์ true
๋ก ๋ณ๊ฒฝํ์ฌ ๋๋๊ทธ ๊ฐ๋ฅํ ์์๊ฐ ๋๋๋ก ํ๋ค. ํ์ฌ ๋๋๊ทธ ํ๊ณ ์๋ ์์๋ฅผ ํ๋ณํ๊ณ ์ฌ์ ๋ ฌํ ๋ ์ฌ์ฉํ๊ธฐ ์ํด ์์์ index
๋ฅผ data ์์ฑ์ ํ ๋นํ๋ค.
data ์์ฑ์ data-๋ฐ์ดํฐ์ด๋ฆ
ํ์์ผ๋ก ์ ์ํ๊ณ , elem.dataset.๋ฐ์ดํฐ์ด๋ฆ
์ผ๋ก ์กฐํํ ์ ์๋ค.
return (
<section>
<ul>
{list.map((item, index) => (
<li draggable={true} data-position={index}>
<span>{item.number}</span>
<p>{item.title}</p>
</li>
))}
</ul>
</section>
);
๋๋๊ทธ ์ด๋ฒคํธ ํธ๋ค๋ฌ
onDragOver์ onDrop ์ด๋ฒคํธ๊ฐ ๊ฑธ๋ ค์๋ DOM์ด ๋๋กญ ๊ฐ๋ฅํ ์์ญ(์ ํจํ ๋๋กญ ๋์)์ด ๋๋ค.
onDragStart
, onDragOver
, onDrop
๋ฐ onDragLeave
(์ ํ) 3๊ฐ์ ์ด๋ฒคํธ๊ฐ ํ์ํ๋ค.
- onDragStart : ๋๋๊ทธ๋ฅผ ์์ํ ๋(๋ง์ฐ์ค๋ฅผ ํด๋ฆญํ ํ ์์ง์ผ ๋) ํธ๋ฆฌ๊ฑฐ
- onDragOver : ๋ง์ฐ์ค ์๋์ ์๋ ์์๊ฐ ์ ํจํ ๋๋กญ ๋์์ผ ๋(์์ดํ
์ด ๋๋กญ๋ ์ ์๋ ๊ณณ) ํธ๋ฆฌ๊ฑฐ
→ ๋ง์ฐ์ค๊ฐ ์ ํจํ ๋๋กญ ๋์ ์์ ์์นํด ์๋ค๋ฉดonDragOver
์ด๋ฒคํธ๊ฐ ์๋ฐฑ ๋ฐ๋ฆฌ ์ด๋ง๋ค ๊ณ์ ํธ๋ฆฌ๊ฑฐ ๋จ - onDrop : ๋๋กญ ๊ฐ๋ฅํ ์์ญ์์ ๋ง์ฐ์ค ํด๋ฆญ์ ํด์ ํ์ฌ ๋๋กญํ์ ๋ ํธ๋ฆฌ๊ฑฐ
- onDragLeave : ๋๋กญ ๊ฐ๋ฅํ ์์ญ์ ๋ฒ์ด๋ฌ์ ๋ ํธ๋ฆฌ๊ฑฐ
- onDragEnter : ๋๋กญ ๊ฐ๋ฅํ ์์ญ์ผ๋ก(๋์ ๊ฐ์ฒด) ์ฒ์ ์ง์ ํ์ ๋ ํธ๋ฆฌ๊ฑฐ
onDragStart — ๋๋๊ทธ ์์
๋๋๊ทธํ๋ ๋์ draggable ์์๋ ๋ฐํฌ๋ช ํํ๋ก ๋ง์ฐ์ค ํฌ์ธํฐ๋ฅผ ๋ฐ๋ผ๋ค๋๋ค
๋ง์ฐ์ค๋ก ์์๋ฅผ ํด๋ฆญํ๊ณ ๋๋๊ทธ๋ฅผ ์์ํ๋ฉด onDragStart
์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ ๋๋ค. ์ด ์ด๋ฒคํธ ํธ๋ค๋ฌ์์ โ๋๋๊ทธํ๊ณ ์๋ ์์์ ์ธ๋ฑ์ค ์ ๋ณด๋ฅผ ์กฐํํ ํ ์ ์ฅํ๊ณ , โ๋๋๊ทธ ์ํ๋ฅผ true
๋ก ๋ณ๊ฒฝํ๊ณ , โํด๋น ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ ์์ ์ ์๋ฆฌ๋จผํธ ๋ฆฌ์คํธ ์ํ๋ฅผ ์ ์ฅํ๋ค.
const onDragStart = (e) => {
const initialPosition = Number(e.target.dataset.position);
setDragAndDrop({
...dragAndDrop,
draggedFrom: initialPosition, // ๋๋๊ทธ๋ฅผ ์์ํ ์์์ ์ธ๋ฑ์ค
isDragging: true,
originalOrder: list, // ํ์ฌ list ์ํ ์ ์ฅ
});
};
onDragOver — ๋๋กญ ๊ฐ๋ฅ ์์ญ โก๏ธ
onDragOver ์ด๋ฒคํธ๋ ์๋ฐฑ ๋ฐ๋ฆฌ์ด๋ง๋ค ๋ฐ๋ํ๊ณ ์ด์ ๋น์ทํ onDragEnter๋ ๋จ ํ๋ฒ๋ง ๋ฐ๋ํ๋ค.
๋๋๊ทธ ์ํ์์ ๋ง์ฐ์ค ํฌ์ธํฐ์ ์๋ ์์๊ฐ ์ ํจํ ๋๋กญ ๋์์ผ ๋ onDragOver
์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋ค. ์ด๋ ์ด๋ฒคํธ target(event.target)์ ๋ง์ฐ์ค ์ปค์ ์๋์ ์์๋ฅผ ๊ฐ๋ฆฌํจ๋ค. ์ด ํธ๋ค๋ฌ์์ ์์๋ฅผ ๋๋กญํ์ ๋ ๋ ๋ํ ๋ฆฌ์คํธ๋ฅผ ๋ฏธ๋ฆฌ ์ค๋นํด๋๋ ์์
(๋ง์ฐ์ค ํฌ์ธํฐ์ ๋ฐ๋ผ ์์ ์์ ๋ณ๊ฒฝ)์ ์ํํ๋ค.
โถ onDragOver
, onDragEnter
์ด๋ฒคํธ๋ ๋๋กญ ์ด๋ฒคํธ๋ฅผ ์ทจ์์ํค๋ ๊ธฐ๋ณธ ์ด๋ฒคํธ๋ฅผ ๊ฐ์ง๋ค. ๋๋กญ ์ด๋ฒคํธ๋ฅผ ์ฌ์ฉํ๊ธฐ ์ํด(๋๋กญ ํ์ฉ) e.preventDefault()
๋ฉ์๋๋ก ๊ธฐ๋ณธ ๋์์ ๋ฐฉ์งํ๋ค — ์ฐธ๊ณ ๋งํฌ
โท ํ์ฌ ๋ง์ฐ์ค๊ฐ hover๋๊ณ ์๋(๊ฐ๋ฆฌํค๊ณ ์๋) ์์ญ์ item ์ธ๋ฑ์ค ์ ์ฅ — draggedTo
โธ ํ์ฌ ๋๋๊ทธํ๊ณ ์๋ ์์๋ฅผ ์ ์ธํ item ๋ฆฌ์คํธ ์ ์ฅ — remainingItems
โน ๋ฆฌ์คํธ ์์ ๋ณ๊ฒฝ. ํ์ฌ ๋๋๊ทธ์ค์ธ ์์ดํ
์ draggedTo
์ธ๋ฑ์ค๋ก(์์น)๋ก ์ถ๊ฐ — updatedOrder
๋๋๊ทธํ๊ณ ์๋ ์์๊ฐ `4`(3๋ฒ ์ธ๋ฑ์ค), ํธ๋ฒ์ค์ธ ์์๊ฐ `2`(1๋ฒ ์ธ๋ฑ์ค)๋ผ๊ณ ๊ฐ์ . ์์ `4`๊ฐ ์์ `2`์ ์์น๋ก ์ด๋ํ๊ณ , ์์ `2`๋ ์์ `4` ๋ค๋ก ๊ฐ๊ฒ๋ ๋ณ๊ฒฝํ๋ค
const list = [1, 2, 3, 4, 5]; // ์ํ๋ ๊ฒฐ๊ณผ๊ฐ [1, 4, 2, 3, 5]
const filtered = list.filter((el) => el !== 4); // [1, 2, 3, 5] ๋๋๊ทธํ๊ณ ์๋ ์์ ์ ์ธ
const from = filtered.slice(0, 1); // [1]
const to = filtered.slice(1); // [2, 3, 5]
const result = [...from, 4, ...to]; // [1, 4, 2, 3, 5]
โบ dragAndDrop ์ํ์ updatedOrder
๋ฐ draggedTo
์
๋ฐ์ดํธ. ๋ง์ฝ dragAndDrop
์ํ์ ์ ์ฅํ draggedTo
์ ํ์ฌ ์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋ผ์ ์ ์ฅํ draggedTo
๊ฐ์ด ๊ฐ๋ค๋ฉด ๋๋๊ทธ์ค๋๋กญ ์ํ ์
๋ฐ์ดํธ๋ ์คํตํ๋ค.
const onDragOver = (e) => {
e.preventDefault(); // onDragOver ๊ธฐ๋ณธ ์ด๋ฒคํธ ๋ฐฉ์ง
const draggedTo = Number(e.target.dataset.position); // ํ์ฌ hover ๋๊ณ ์๋(๋ง์ฐ์ค๊ฐ ์์นํ) item์ ์ธ๋ฑ์ค
const { originalOrder, draggedFrom } = dragAndDrop; // ๊ธฐ์กด ๋ฆฌ์คํธ ๋ฐ ๋๋๊ทธ์ค์ธ ์์์ ์ธ๋ฑ์ค ์กฐํ
const remainingItems = originalOrder.filter(
(_, index) => index !== draggedFrom, // ํ์ฌ ๋๋๊ทธ ํ๊ณ ์๋ ์์๋ฅผ ์ ์ธํ items ๋ชฉ๋ก
);
// ๋ฆฌ์คํธ ์์ ๋ณ๊ฒฝ.
// ํ์ฌ ๋๋๊ทธ์ค์ธ ์์ดํ
์ draggedTo(ํ์ฌ ๋ง์ฐ์ค๊ฐ ์์นํ) ์ธ๋ฑ์ค ์์น๋ก ์ถ๊ฐ
const updatedOrder = [
...remainingItems.slice(0, draggedTo),
originalOrder[draggedFrom], // ํ์ฌ ๋๋๊ทธ์ค์ธ ์์ดํ
...remainingItems.slice(draggedTo),
];
// ์์ ๋ณ๊ฒฝํ ๋ฆฌ์คํธ ๋ฐ hover ์์์ ์ธ๋ฑ์ค(draggedTo) ์
๋ฐ์ดํธ.
// ์ํ์ ์ ์ฅํ hover ์์ ์ธ๋ฑ์ค์ ๊ฐ์ง ์์๋๋ง ์
๋ฐ์ดํธํ๋ค
if (draggedTo !== dragAndDrop.draggedTo) {
setDragAndDrop({
...dragAndDrop,
updatedOrder, // ๋๋กญ ์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋ฉด ํด๋น ๋ฆฌ์คํธ๊ฐ ๋ ๋๋จ
draggedTo,
});
}
};
onDrop — ๋๋กญ
๋๋กญ ๊ฐ๋ฅํ ์์ญ์์ ๋๋กญํ์ ๋ onDrop
์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋ค. onDropOver
์์ ์์
ํ๋, ์์๋ฅผ ๋ณ๊ฒฝํ ๋ฆฌ์คํธ๋ฅผ ๋ ๋๋ ๋ฆฌ์คํธ(list)๋ก ์
๋ฐ์ดํธํ๊ณ dragAndDrop
์ํ๋ฅผ ์ด๊ธฐํํ๋ค.
const onDrop = () => {
// onDropOver์์ ์์
ํด๋(๋ง์ฐ์ค ์ปค์์ ๋ฐ๋ผ ์์๋ฅผ ๋ณ๊ฒฝํ) ์์ ๋ฆฌ์คํธ ์
๋ฐ์ดํธ
setList(dragAndDrop.updatedOrder);
// dragAndDrop ์ํ ์ด๊ธฐํ
setDragAndDrop({
...dragAndDrop,
draggedFrom: null,
draggedTo: null,
isDragging: false,
});
};
onDragLeave
๋๋กญ ๊ฐ๋ฅํ ์์ญ์ ๋ฒ์ด๋ฌ์ ๋ onDragLeave
์ด๋ฒคํธ๊ฐ ํธ๋ฆฌ๊ฑฐ๋๋ค. ๋๋กญ ๋์ ์์์ ๋ํ ์ธ๋ฑ์ค๋ฅผ ๋ด๋ ์ํ์ธ DraggedTo
์ํ ๊ฐ์ null
๋ก ์ค์ ํ๋ค — ๋๋กญ ๊ฐ๋ฅํ ์์ญ์ ๋ฒ์ด๋์ ๋๋กญ ๊ฐ๋ฅํ ์์ ์์ฒด๊ฐ ์์ผ๋ฏ๋ก
const onDragLeave = () => {
setDragAndDrop({
...dragAndDrop,
draggedTo: null,
});
};
ํธ๋ค๋ฌ ํ ๋น
์์์ ์์ฑํ ํธ๋ค๋ฌ๋ฅผ Drop ์์ญ์ด ๋ ๊ณณ์ธ <li>
ํ๊ทธ์ ํ ๋นํ๋ค. onDragOver
์ onDrop
์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ๋ฉด, ํด๋น ์์๋ ๋๋กญ ๊ฐ๋ฅํ ์์ญ์ด ๋๋ค.
return (
<section>
<ul>
{list.map((item, index) => (
<li
// ...
onDragStart={onDragStart}
onDragLeave={onDragLeave}
onDragOver={onDragOver}
onDrop={onDrop}
></li>
// ...
))}
</ul>
</section>
);
CSS
Placeholder ์คํ์ผ
๋๋กญํ ์์น์ ์ ์ ๋ชจ์์ Placeholder ์คํ์ผ์ ์ ์ฉํด ์๊ฐ์ ํํธ๋ฅผ ์ค ์ ์๋ค. draggedTo
์ํ(๋๋๊ทธ๋ฅผ ์์ํ ํ ๋ง์ฐ์ค ์ปค์๊ฐ ์์นํ ์์์ ์ธ๋ฑ์ค)์ ๋ ๋ํ ์์์ ์ธ๋ฑ์ค๊ฐ ๊ฐ๋ค๋ฉด .dropArea
ํด๋์ค๋ฅผ ์ถ๊ฐํ๊ณ , ํด๋น ํด๋์ค์ ๋ํ placeholder ์คํ์ผ์ ์ง์ ํ๋ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํํ ์ ์๋ค.
return (
<section>
<ul>
{list.map((item, index) => (
<li
className={dragAndDrop?.draggedTo === Number(index) ? "dropArea" : ""}
// ...
></li>
// ...
))}
</ul>
</section>
);
์คํ์ผ์ ์ํ ๋ชฉ์ ์ด๋ฏ๋ก ๋ฐ๋ก ํ๊ทธ๋ฅผ ์ถ๊ฐํ์ง ์๊ณ ::before
์๋ ์๋ฆฌ๋จผํธ๋ฅผ ์ด์ฉํ๋ค. ๋๋๊ทธ๋ฅผ ์์ํ์ฌ ๋ง์ฐ์ค ์ปค์๊ฐ ์์นํ ์์์ <li>
์คํ์ผ์ .dropArea
ํด๋์ค ์คํ์ผ์ด ์ ์ฉ๋๋ค.
li {
// ...
&.dropArea {
color: white !important; // ์ด๋ชจํฐ์ฝ์ ์ ์ธํ ํ
์คํธ ๋ชจ๋ ํฐ์์ผ๋ก
background: white !important;
position: relative;
&::before {
content: "Drop Here";
color: "#687bf7";
font-size: 0.5em;
text-transform: uppercase; // ๋๋ฌธ์
width: 100%;
height: 100%;
border: 2px dashed "#687bf7"; // ์ ์
border-radius: 3px;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}
span {
display: none; // ์์ดํ
๋๋ฒ ๊ฐ์ถ๊ธฐ
}
p {
margin-left: 1em;
}
}
// ...
}
Margin ์ฌ๋ฐฑ ์ ๋๋ฉ์ด์
transition
์์ฑ์ ํ์ฉํด ๋ง์ฐ์ค๋ฅผ ๋๋๊ทธํ์ฌ Placeholder ์คํ์ผ์ด ์ ์ฉ๋๊ณ , ๋ค์ ๊ธฐ์กด <li>
์คํ์ผ๋ก ๋์์ฌ ๋๋ง๋ค ์์์ ํ
์คํธ๊ฐ ์์ง์ด๋ ๋ฏํ ํจ๊ณผ๋ฅผ ์ค ์๋ ์๋ค.
li {
p {
// margin-left๊ฐ ์ ์ฉ๋๊ธฐ๊น์ง์ ์๊ฐ์ 50ms๋ก ์ค์
transition: margin-left 50ms ease-in-out;
}
}
๋ ํผ๋ฐ์ค
๊ธ ์์ ์ฌํญ์ ๋ ธ์ ํ์ด์ง์ ๊ฐ์ฅ ๋น ๋ฅด๊ฒ ๋ฐ์๋ฉ๋๋ค. ๋งํฌ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์
'๐ช Programming' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
๋๊ธ
์ด ๊ธ ๊ณต์ ํ๊ธฐ
-
๊ตฌ๋
ํ๊ธฐ
๊ตฌ๋ ํ๊ธฐ
-
์นด์นด์คํก
์นด์นด์คํก
-
๋ผ์ธ
๋ผ์ธ
-
ํธ์ํฐ
ํธ์ํฐ
-
Facebook
Facebook
-
์นด์นด์ค์คํ ๋ฆฌ
์นด์นด์ค์คํ ๋ฆฌ
-
๋ฐด๋
๋ฐด๋
-
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
๋ค์ด๋ฒ ๋ธ๋ก๊ทธ
-
Pocket
Pocket
-
Evernote
Evernote
๋ค๋ฅธ ๊ธ
-
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฐ์ด์ค Debounce, ์ค๋กํ Throttle ๊ตฌํํ๊ธฐ
[JS] ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฐ์ด์ค Debounce, ์ค๋กํ Throttle ๊ตฌํํ๊ธฐ
2024.04.29 -
[Git] Github ๋งํฌ๋ค์ด์ ๊ฐ์ฃผ ๋ฌ๊ธฐ
[Git] Github ๋งํฌ๋ค์ด์ ๊ฐ์ฃผ ๋ฌ๊ธฐ
2024.04.29 -
[Algorithm] ํน์ ์ ๊น์ง์ ํฉ ๊ตฌํ๊ธฐ / ๋ฑ์ฐจ์์ด (๊ฐ์ฐ์ค ๊ณต์)
[Algorithm] ํน์ ์ ๊น์ง์ ํฉ ๊ตฌํ๊ธฐ / ๋ฑ์ฐจ์์ด (๊ฐ์ฐ์ค ๊ณต์)
2024.04.28 -
[CS] ์ง๋ฒ ๊ณ์ฐ ๋ฐฉ๋ฒ — 10์ง์ โ 2์ง์ ๋ณํ
[CS] ์ง๋ฒ ๊ณ์ฐ ๋ฐฉ๋ฒ — 10์ง์ โ 2์ง์ ๋ณํ
2024.04.28