MotionDnDGrid
MotionDnDGrid
컴포넌트는 드래그 앤 드롭으로 그리드 아이템을 재정렬할 수 있는 리스트를 만들 때 사용해요.
import { MotionDnDGrid } from '@simple-motion-dnd/grid';
import { useState } from 'react';
const [items, setItems] = useState([
{ id: 1 },
{ id: 2 },
{ id: 3 },
{ id: 4 },
{ id: 5 },
{ id: 6 },
]);
return (
<MotionDnDGrid.Group items={items} onSorted={setItems} cols={3} gap={10}>
{items.map((item) => (
<MotionDnDGrid.Item key={item.id} itemId={item.id}>
<div>{item.id}</div>
</MotionDnDGrid.Item>
))}
</MotionDnDGrid.Group>
);
참고:
MotionDnDGrid
는 가볍고 직관적인 그리드 기반 DnD 구현을 목표로 설계되었어요. 다중 행 간 드래그, 영역 간 이동, 복잡한 트리 구조 같은 고급 기능은 지원하지 않아요. 이런 기능이 필요하다면 DnD Kit 같은 고급 라이브러리를 추천해요.
사용법
모든 재정렬 가능한 그리드는 MotionDnDGrid.Group
컴포넌트로 감싸야 해요.
이 컴포넌트는 드래그 가능한 아이템들의 위치와 정렬 상태를 관리해요.
import { MotionDnDGrid } from '@simple-motion-dnd/grid';
function Grid() {
return <MotionDnDGrid.Group></MotionDnDGrid.Group>;
}
기본적으로 <ul>
요소로 렌더링되지만, as
prop으로 변경할 수 있어요.
<MotionDnDGrid.Group as="div">
MotionDnDGrid.Group
에는 반드시 items
prop으로 아이템 리스트를 전달해야 해요.
또한 아이템 순서가 변경되면 onSorted
이벤트가 새 순서와 함께 호출돼요.
const [items, setItems] = useState([{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }]);
<MotionDnDGrid.Group items={items} onSorted={setItems} cols={2} gap={10} />;
각 재정렬 가능한 아이템을 렌더링하려면 MotionDnDGrid.Item
을 사용하고, itemId
prop으로 해당 아이템의 고유 ID를 전달해요.
<MotionDnDGrid.Group items={items} onSorted={setItems} cols={2} gap={10}>
{items.map((item) => (
<MotionDnDGrid.Item key={item.id} itemId={item.id}>
<div>{item.id}</div>
</MotionDnDGrid.Item>
))}
</MotionDnDGrid.Group>
이제 아이템을 드래그하면 onSorted
가 새로운 순서와 함께 호출돼요.
스크롤 가능한 그리드
MotionDnDGrid.Group
을 스크롤 가능한 컨테이너에서 사용한다면,
Framer Motion이 올바르게 레이아웃을 측정할 수 있도록 layoutScroll
prop을 추가해야 해요.
<MotionDnDGrid.Group
layoutScroll
items={items}
onSorted={setItems}
cols={3}
gap={10}
style={{ overflowY: 'auto', height: '400px' }}>
{items.map((item) => (
<MotionDnDGrid.Item key={item.id} itemId={item.id}>
<div>{item.id}</div>
</MotionDnDGrid.Item>
))}
</MotionDnDGrid.Group>
드래그 트리거
기본적으로 MotionDnDGrid.Item
의 전체 영역이 드래그 가능해요.
특정 영역만 드래그 가능하게 하려면 useDragControls
를 사용하면 돼요.
import { MotionDnDGrid, useDragControls } from '@simple-motion-dnd/grid';
function Item({ itemId }) {
const controls = useDragControls();
return (
<MotionDnDGrid.Item itemId={itemId} dragListener={false} dragControls={controls}>
<div
className="drag-handle cursor-grab"
onPointerDown={(e) => controls.start(e)}>
드래그 핸들
</div>
<div className="content">아이템 내용</div>
</MotionDnDGrid.Item>
);
}
z-index
MotionDnDGrid.Item
은 드래그 중인 아이템이 다른 아이템 위로 표시되도록
자동으로 z-index
스타일을 적용해요. 내부적으로 position: relative
로 설정되어 있어,
드래그 중에도 자연스럽게 위로 떠오르는 효과를 볼 수 있어요.
API Reference
MotionDnDGrid.Group
Prop | 타입 | 기본값 | 설명 |
---|---|---|---|
as | keyof HTMLElementTagNameMap | 'ul' | 렌더링할 HTML 태그를 지정해요. |
items | T[] | — | 재정렬될 아이템 배열이에요. 각 아이템은 id 속성을 포함해야 해요. |
onSorted | (newOrder: T[]) => void | — | 순서가 변경될 때 호출돼요. |
cols | number | — | 한 줄에 표시할 열의 개수예요. |
gap | number | — | 아이템 간의 간격(px 단위)이에요. |
layoutScroll | boolean | false | 스크롤 가능한 컨테이너에서 사용 시 true 로 설정해요. |
MotionDnDGrid.Item
Prop | 타입 | 기본값 | 설명 |
---|---|---|---|
as | keyof HTMLElementTagNameMap | 'li' | 렌더링할 HTML 태그를 지정해요. |
itemId | string | number | — | 아이템의 고유 ID예요. |
onDragStart | (event, info) => void | — | 드래그 시작 시 호출돼요. |
onDrag | (event, info) => void | — | 드래그 중 호출돼요. |
onDragEnd | (event, info) => void | — | 드래그 종료 시 호출돼요. |
Motion Props 지원
MotionDnDGrid.Group
과 MotionDnDGrid.Item
은 Motion의 motion 컴포넌트 위에 구축되어 있어서,
모든 motion
관련 props(layout
, transition
, drag
, whileHover
등)를 그대로 사용할 수 있어요.
요약
MotionDnDGrid.Group
으로 리스트를 감싸고, 내부에MotionDnDGrid.Item
을 배치하면 드래그 앤 드롭 정렬이 가능해요.onSorted
를 통해 변경된 배열을 상위 상태로 전달해요.- 스크롤, 터치, 마우스 모두 지원하고, 네이티브 DOM 기반의 충돌 감지로 빠르게 동작해요.