์๋ฌธ:https://github.com/atlassian/react-beautiful-dnd
์ต์ข
๋ฒ์ญ ์ผ์: 2017๋
08์23์ผ(์) 22์00๋ถ UTC+9 ์๋ณธ
์๋ฆ๋ต๊ณ ์ ๊ทผ ์ฉ์ดํ Rreact(React.js
) ๋ฆฌ์คํธ ๋๋๊ทธ ์ค ๋๋
์ผ๋ง๋ ์๋ฆ๋ค์ด์ง ์ง์ ๋ณด์ธ์ - have a play with the examples!
- ์๋ฆ๋ต๊ณ ์์ฐ์ค๋ฌ์ด ์์ดํ ์ด๋
- ๊น๋ํ๊ณ ๊ฐ๋ ฅํ API ๋ฅผ ํตํ ๊ฐ๋จํ ์ฌ์ฉ
- ๋ ์ฐฉ์ ์ธ ์คํ์ผ
- ์ถ๊ฐ DOM ์์ฑ์ด ํ์ ์์ - ์น์ํ flexbox ์ focus ๊ด๋ฆฌ
- anchor ํ๊ทธ ๋ฑ ๊ธฐ์กด ๋ ธ๋์ ์ ์๋
- ์ํ(state) ์ฃผ๋์ ๋๋๊น - ์ด ๋ถ๋ถ์ ํ๋ก๊ทธ๋๋ฐ(programatic) ๋ฐฉ์์ผ๋ก ๋ง์ input ํ์ ์์ ๋๋๊น (dragging)์ ํ์ฉ ํ๋ค. ํ์ฌ๋ ๋ง์ฐ์ค ์ ํค๋ณด๋ ๋๋๊น (dragging)๋ง ์ง์ํฉ๋๋ค.
React๋ฅผ ์ฌ์ฉํ ๋ง์ ๋๋๊ทธ ์ค ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ต๋๋ค. ๊ทธ์ค ๊ฐ์ฅ ์ฃผ๋ชฉํ ๊ฒ์ react-dnd
์
๋๋ค. ๊ทธ๊ฒ์ HTML5 ๋๋๊ทธ ์ค ๋๋ feature(wildly inconsistent)๊ณผ ํ๋ฅญํ๊ฒ ๋งค์น๋๋ ๋๋๊ทธ ์ค ๋๋์ ์ง์ ํฉ๋๋ค. react-beautiful-dnd
๋ vertical ๊ณผ horizontal ๋ฆฌ์คํธ๋ฅผ ์ํด ํน๋ณํ high level๋ก ์ถ์ํ ๋์ด์์ต๋๋ค.
react-beautiful-dnd
๋ ๊ธฐ๋ฅ์ ์ธ subset์ผ๋ก ํ์ํํ๊ณ ์์ฐ์ค๋ฝ๊ณ ์๋ฆ๋ค์ด ๋๋๊ทธ ์ค ๋๋์ ์ ์ํฉ๋๋ค. ๊ทธ๋ฌ๋, ๊ทธ๊ฒ์ react-dnd ์ฒ๋ผ ๋ง์ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํ์ง ์์ต๋๋ค. ๊ทธ๋์ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋น์ ์ ๊ฒฝ์ฐ์ ๋ง์ง ์์ ์ ์์ต๋๋ค.
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์๊ฒ์ด๊ธฐ ๋๋ฌธ์ ์๋์ ์ผ๋ก ์์ ๊ธฐ๋ฅ๋ง ์ ๊ณต ํฉ๋๋ค. ๊ธฐ๋ฌ๋ ค ์ฃผ์ธ์! ๋น ๋ฅด๊ฒ ์์ง์ผ ๊ฒ์ ๋๋ค!
- vertical ๋ฆฌ์คํธ
- horizontal ๋ฆฌ์คํธ
- ํ ํ์ด์ง ์์ ๋ฉํฐ ๋ ๋ฆฝ ๋ฆฌ์คํธ
- ๋ง์ฐ์ค ๐ญ ์ ํค๋ณด๋ ๐น ๋๋๊น
- ๋ ๋ฆฝ ์ค์ฒฉ ๋ฆฌ์คํธ(๋ฆฌ์คํธ๋ ๋ค๋ฅธ ๋ฆฌ์คํธ์ ์์์ด ๋ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋น์ ์ ๋ถ๋ชจ ๋ฆฌ์คํธ์ ์์ดํ ์ ์์ ๋ฆฌ์คํธ๋ก ๋๋๊ทธ ํ ์ ์์ต๋๋ค.)
- ๊ฐ๋ณ ๋์ด ์์ดํ (์์ดํ ์ ์๋ก ๋ค๋ฅธ ๋์ด๋ฅผ ๊ฐ์ง์ ์๋ค.)
- ์ปค์คํ ๋๋๊ทธ ํธ๋ค (์ผ๋ถ ์์ดํ ๋ง ๋๋๊ทธ ํ ์ ์๋ค.)
- vertical ๋ฆฌ์คํธ๋ scoll container๊ฐ ๋ ์ ์๋ค. (์คํฌ๋กค ๋ถ๋ชจ ์ปจํ ์ด๋ ์์ด) ํน์ ์์์ด scoll container๊ฐ ๋ ์ ์๋ค. (์ญ์ ์คํฌ๋กค ๊ฐ๋ฅํ ๋ถ๋ชจ ์๋ค)
์ด๊ฒ์ ๊ฐ๋จํ ์ฌ์ ๋ ฌ ๋ฆฌ์คํธ ์ ๋๋ค. You can play with it on webpackbin
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd';
// fake data generator(๊ฐ์ง ๋ฐ์ดํฐ ์ ๋๋ ์ดํฐ)
const getItems = (count) => Array.from({length: count}, (v, k) => k).map(k => ({
id: `item-${k}`,
content: `item ${k}`
}));
// a little function to help us with reordering the result(๊ฒฐ๊ณผ ์ฌ์ ๋ ฌ์ ๋๋ ํจ์)
const reorder = (list, startIndex, endIndex) => {
const result = Array.from(list);
const [removed] = result.splice(startIndex, 1);
result.splice(endIndex, 0, removed);
return result;
};
// using some little inline style helpers to make the app look okay(๋ณด๊ธฐ์ข๊ฒ ์ฑ์ ๋ง๋๋ ์ธ๋ผ์ธ ์คํ์ผ ํฌํผ)
const grid = 8;
const getItemStyle = (draggableStyle, isDragging) => ({
// some basic styles to make the items look a bit nicer(์์ดํ
์ ๋ณด๊ธฐ ์ข๊ฒ ๋ง๋๋ ๋ช ๊ฐ์ง ๊ธฐ๋ณธ ์คํ์ผ)
userSelect: 'none',
padding: grid * 2,
marginBottom: grid,
// change background colour if dragging(๋๋๊น
์ ๋ฐฐ๊ฒฝ์ ๋ณ๊ฒฝ)
background: isDragging ? 'lightgreen' : 'grey',
// styles we need to apply on draggables(๋๋๊ทธ์ ํ์ํ ์คํ์ผ ์ ์ฉ)
...draggableStyle
});
const getListStyle = (isDraggingOver) => ({
background: isDraggingOver ? 'lightblue' : 'lightgrey',
padding: grid,
width: 250
});
class App extends Component {
constructor(props) {
super(props);
this.state = {
items: getItems(10)
}
this.onDragEnd = this.onDragEnd.bind(this);
}
onDragEnd (result) {
// dropped outside the list(๋ฆฌ์คํธ ๋ฐ์ผ๋ก ๋๋ํ ๊ฒฝ์ฐ)
if(!result.destination) {
return;
}
const items = reorder(
this.state.items,
result.source.index,
result.destination.index
);
this.setState({
items
});
}
// Normally you would want to split things out into separate components.(์ผ๋ฐ์ ์ผ๋ก ๋น์ ์ ์ปดํฌ๋ํธ๋ฅผ ๋๋ ๊ฒ์
๋๋ค.)
// But in this example everything is just done in one place for simplicity(๊ทธ๋ฌ๋ ์์ ๋ฅผ ๊ฐ๋จํ๊ฒ ํ๊ธฐ ์ํด ํ๊ณณ์ ์ ์ฉํ์ต๋๋ค.)
render() {
return (
<DragDropContext onDragEnd={this.onDragEnd}>
<Droppable droppableId="droppable">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={getListStyle(snapshot.isDraggingOver)}
>
{this.state.items.map(item => (
<Draggable
key={item.id}
draggableId={item.id}
>
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={getItemStyle(
provided.draggableStyle,
snapshot.isDragging
)}
{...provided.dragHandleProps}
>
{item.content}
</div>
{provided.placeholder}
</div>
)}
</Draggable>
))}
</div>
)}
</Droppable>
</DragDropContext>
);
}
}
// Put the thing into the DOM!(DOM ์ ์ฑ ์ ์ฉ)
ReactDOM.render(<App />, document.getElementById('app'));
react-beautiful-dnd
์ ํต์ฌ ๋์์ธ์ ๋ฌผ๋ฆฌ์ ์
๋๋ค: ์ฐ๋ฆฌ๋ ์ฌ์ฉ์๊ฐ ๊ทธ๋ค์ด ๋ฌผ๋ฆฌ์ ์ค๋ธ์ ํธ๋ฅผ ์์ง์ธ๋ค๊ณ ๋๋ผ๊ธธ ์ํฉ๋๋ค.
์ด๊ฒ์ ์ฌ์ฉ์ ๋๋๊ทธ์ ๋ฐ์ํ๋ ๊ฒ์ ํ์ค ๋๋๊ทธ ์ค ๋๋ ํจํด์ ๋๋ค. ๋ณด๋ค์ด์์ฐ์ค๋ฌ์ด ๋๋๊ทธ ์ ๋๋ฉ์ด์ ์ ์ํด ๋ช ํํ ๋๋๊ทธ ํจ๊ณผ๋ฅผ ๋ณด์ฌ์ค๋๋ค. ์ฐ๋ฆฌ๋ ์ญ์ ์๋ก์ด ์์น๋ก ์์ดํ ์ ๋๋ํ ์ ์๊ฒ ํฉ๋๋ค. ์๋ฌด ๊ณณ์ผ๋ก๋ ์์ดํ ์ ์ด๋ํ ์๋ ์์ต๋๋ค. ๋๋๊ทธ ํ๊ณ ๋ง๊ณ ๋ ๊ด๋ จ ์์ต๋๋ค.
๋๋๊ทธ ์ค ๋๋ ์ธํฐ๋ ์ ์ ์ฌ์ฉ์๊ฐ ๋๋๊ทธ ์์ํ ๊ณณ๋ถํฐ ์์ํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ ๋๋ค.
react-beautiful-dnd
์ ์์ดํ
๋๋๊น
ํจ๊ณผ๋ ์์ดํ
์ ์ค๋ ฅ ํจ๊ณผ๋ฅผ ๊ธฐ๋ณธ์ผ๋ก ํฉ๋๋ค. - ๋น์ ์ด ์์ดํ
์ ์ด๋์์ ์ก์๋์ง๋ ๊ด๊ณ ์์ต๋๋ค. ์์ดํ
๋๋๊น
ํจ๊ณผ๋ ์ค์ผ์ผ ์ค์ โ๏ธ๊ณผ ์ ์ฌํ ๊ท์น์ ๋ฐ๋ฆ
๋๋ค. ๊ฐ๋ณ ๋์ด ์์ดํ
์ ์์ฐ์ค๋ฌ์ด ๋๋๊ทธ๋ฅผ ์ํด ๋ช ๊ฐ์ง ๊ท์น์ ํ์ฉ ํฉ๋๋ค.
- ๋ฆฌ์คํธ๋ ๋ฆฌ์คํธ์ ๋ฐ์ด๋๋ฆฌ๊น์ง ์์ดํ ๋๋๊น ๊ฐ๋ฅํฉ๋๋ค.
- ๋๋๊ทธ ์์ดํ ์ ์ค์ฌ ํฌ์ง์ ์ด ์์ดํ ์ ๊ฒฝ๊ฒ๋ก ๋๋๊ทธ ๋๋ฉด ๋๋จธ์ง ์์ดํ ๋ค์ ์์ง์ด๊ฒ ๋ฉ๋๋ค. ๋ค๋ฅด๊ฒ ๋งํ์๋ฉด: (A) ์์ดํ ์ ์ค์ฌ ํฌ์ง์ ์ด ๋์ผ๋ก ์ด๋๋๋ฉด ๋ค๋ฅธ ์์ดํ (B)๋ ์์ง์ด๊ฒ ๋ฉ๋๋ค.
๋๋ ์๋์ฐ๋ ์์ดํ
๊ณผ ๊ทธ๊ฒ์ ๋ชฉํ๊ฐ ๋ฐ๋๋ ํ๊ฒฝ์์ ์ ์ฉํฉ๋๋ค. ๊ทธ๋ฌ๋ react-beautiful-dnd
๋ ์ด๋์ ์์ดํ
์ด ๋๋๋ ์ง๊ฐ ๋ช
ํํด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ์ถํ์ ๋ณ๊ฒฝ๋ ๊ฒ์
๋๋ค. - ๊ทธ๋ฌ๋ ์ด๋ฐ ํจ๊ณผ ์์ด ์ด๋ ๊น์ง ๋ณผ์ ์์์ง ์คํํด ๋ด์ผ ํฉ๋๋ค.
react-beautiful-dnd
๋ ์ต๋ํ ์ธํฐ๋ ํฐ๋ธ๊ฐ ๋ถ๊ฐ๋ฅํ ๊ธฐ๊ฐ์ ํผํ๋ ค๊ณ ๋
ธ๋ ฅํฉ๋๋ค. ๊ทธ๋์ ์ฌ์ฉ์๋ ์ ๋๋ฉ์ด์
์ด ๋๋๊ธฐ๋ฅผ ๊ธฐ๋ค๋ฆด ํ์ ์์ด ์ง์์ ์ผ๋ก ์ธํฐํ์ด์ค๋ฅผ ํตํด ์ธํฐ๋ ์
ํ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋, ๋ชจ๋๊ฐ ๋ถ๋ณํ ์ ์๊ฒ ์ ํ์ฑ๊ณผ ํ ์ฌ์ด์์ ๊ท ํ์ ์ ์งํด์ผ ํฉ๋๋ค. ์ฌ๊ธฐ์ ์ธํฐ๋ ํฐ๋ธ ํ์ง ์์ ๋ช ๊ฐ์ง ์ํฉ์ด ์์ต๋๋ค.
- ์ฌ์ฉ์๊ฐ ๋๋ ์ ๋๋ฉ์ด์ ์ ์๋ฃํ ๋ ๋๋๊ทธ๋ฅผ ์ทจ์ํ ๊ฒฝ์ฐ, ๊ทธ๋ค์ ์ต๋ํ ๋๋๋ฆฝ๋๋ค. ์ฌ๋ฐ๋ฅด์ง ์์ ์์น์ ๋๋๊ทธ๋ฅผ ํ๋ฉด ์๋ฉ๋๋ค.
- ๋๋๋๋ ์์ดํ ์ด ๋๋๊ทธ ๋ฉ๋๋ค. ๊ฐ๋จํ๊ฒ ์ด ๊ฒฝ์ฐ ์ ๋๋ค - ์๋ ์์น๋ก ์์ง์ด๊ณ ์๋ ์์ดํ ์ ์ก๊ธฐ๋ ์์ฒญ ํ๋ญ๋๋ค. ์ด๊ฒ์ ์ฝ๋ฉ ๊ฐ๋ฅํฉ๋๋ค - ๊ทธ๋ฌ๋ ๋ง์ ๋ณต์กํ ์ผ์ด์ค๊ฐ ์์ ๊ฒ์ ๋๋ค.
ํญ์ ๋นํ์ฑ ๊ธฐ๊ฐ์ด ์กด์ฌํ๋ ๊ฒ์ ์๋๋๋ค.
์ง๊ธ ๊น์ง ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋๋๊ทธ ์ถ ์๊ธ์ ์ง์ํ์ง ์์์ต๋๋ค. (๋๋๊ทธ ๋ ์ผ์ค(rails)๋ผ๊ณ ๋ ๋ถ๋ฆฝ๋๋ค.) ์ด๊ฒ์ ์ฌ์ฉ์๊ฐ ํ ์ถ์ผ๋ก๋ง ๋๋๊ทธ ๊ฐ๋ฅํ๋๋ก ์ ์ํ๋ ๊ฒ์
๋๋ค. ํ์ฌ ์๊ฐ์ผ๋ก๋ ์ด๊ฒ์ ๋ฌผ๋ฆฌ์ ๋น์ ๋ฅผ ํค์นฉ๋๋ค. ์ฐ๋ฆฌ๋ ๋ฌผ๋ฆฐ์ ์ธ ๋ฌผ์ฒด๋ฅผ ์์ง์ด๋ ๊ฒ์ด ์๋๋ผ ๋ฉ์ธ์ง๋ฅผ ์ฌ์ฉ์์๊ฒ ๋ณด๋ด๋ฉด์ ์ํธ์์ฉ ํฉ๋๋ค. ๊ทธ๊ฒ์ ์ฌ์ฉ์๊ฐ type
๊ณผ isDropEnable
props๋ฅผ ์ฌ์ฉํ์ฌ ํ๋์ ๋ฆฌ์คํธ์๋ง ๋๋ํ ์ ์๋๋ก ๊ฐ๋ฅํฉ๋๋ค. ๋น์ ์ onDragStart
๋ฆฌ์คํธ ์๊ฐ์ ์ผ๋ก ์ฒ๋ฆฌํ์ฌ ์ธํฐ๋ ํธํ ์ ์๋ ์ ์ผํ ์ฅ์์์ ๋ณด์ฌ์ค ์ ์์ต๋๋ค.
์ฌ์ฉ์๊ฐ ์๋ฆฌ๋ฉํธ์ ๋ง์ฐ์ค ๋ค์ด์ ๋๋ฅผ ๊ฒฝ์ฐ, ์ฐ๋ฆฌ๋ ์ฌ์ฉ์๊ฐ ํด๋ฆญ์ ํ ๊ฑด์ง ๋๋๊ทธ๋ฅผ ํ๋ ๊ฑด์ง ์ ์ ์์ต๋๋ค. ๋๋๋ก ์ฌ์ฉ์๊ฐ ํด๋ฆญํ ๋ ์ปค์๋ฅผ ์ฝ๊ฐ ์์ง์ด๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. - ์์ฑํ ํด๋ฆญ. ๊ทธ๋์ ์ฐ๋ฆฌ๋ ์ผ์ ํ ๊ฑฐ๋ฆฌ๋ฅผ ๋ง์ฐ์ค ๋ค์ด๊ณผ ํจ๊ป ์์ง์ผ ๊ฒฝ์ฐ ๋๋๊ทธ๋ฅผ ์์ํฉ๋๋ค. - ์์ฑํ ํด๋ฆญ์ ๋ง๋๋ ๊ฒ๋ณด๋ค ๋ซ์ต๋๋ค. ๋ง์ฝ ๋๋๊ทธ ํ๊ณ์์ ์ ๋๊ธฐ์ง ์๊ฒ ๋๋ฉด ์ฌ์ฉ์ ์ธํฐ๋ ์ ์ ์ผ๋ฐ์ ์ธ ํด๋ฆญ์ผ๋ก ์๋ํฉ๋๋ค. ๋ง์ฝ ๋๋๊ทธ ํ๊ณ์์ ์ด ์ง๋๋ฉด ์ธํฐ๋ ์ ์ ๋๋๊ทธ ๋๊ณ ๊ธฐ๋ณธ ํด๋ฆญ ์ก์ ์ ์ผ์ด๋์ง ์์ต๋๋ค.
์ด๊ฒ์ ์ฌ์ฉ์๊ฐ ์ธํฐ๋ ํฐ๋ธ ์๋ฆฌ๋ฒํธ๋ฅผ ๋ํํ ์ ์๊ฒ ํ๋ฉฐ ์์ฐ์ค๋ฌ์ด ๋ฐฉ์์ผ๋ก ๊ธฐ๋ณธ ์ต์ปค๋ฟ์ ๋๋๊ทธ ๊ฐ๋ฅํ ์์ดํ ์ ๊ฐ์ง ์ ์๊ฒ ํฉ๋๋ค.
(๐ฑ๐ is a schrodinger's cat joke)
react-beautiful-dnd
์ ์ด๋ค ๋ํผ ์๋ฆฌ๋จผํธ๋ ์์ฑํ์ง ์์ต๋๋ค. ์ฆ ๋ฌธ์์ ์ผ๋ฐ์ ์ธ ํญ ํ๋ฆ์ ์ํฅ์ ๋ฏธ์น์ง ์์ต๋๋ค. ์๋ฅผ ๋ค์ด anchor(์ต์ปค) ํ๊ทธ๋ฅผ ๋ํํ๋ ๊ฒฝ์ฐ ์ฌ์ฉ์๋ *anchor(์ต์ปค)*๋ฅผ ๋๋ฌ์ผ ์์๊ฐ ์๋ ์ต์ปค์ ์ง์ ํญ์ ๊ฒ๋๋ค. ๋น์ ์ด ์ด๋ค ์๋ฆฌ๋จผํธ์ ๋ํ์ ํ๋์ง ํค๋ณด๋ ๋๋๊น
์ ์ํด tab-index
๊ฐ ์ฃผ์ด์ง๋๋ค.
์ ํต์ ์ผ๋ก ๋๋๊ทธ ์ค ๋๋ ์ธํฐ๋ ์ ์ ์ค์ง ๋ง์ฐ์ค์ ํฐ์น ์ธํฐ๋ ์ ๋ง ์์ต๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ using only a keyboard(์ค์ง ํค๋ณด๋๋ฅผ ํตํ) ๋๋๊ทธ ์ธํฐ๋ ์ ๋ ์ง์ํฉ๋๋ค. ์ด๋ฅผ ํตํด ๊ณ ๊ธ ์ฌ์ฉ์๋ ํค๋ณด๋๋ฅผ ํตํด ๋๋๊ทธ ์ค ๋๋์ ๊ฒฝํํ ์ ์์ต๋๋ค. ๋ํ ์ด์ ์ ์ ์ธ ๋์๋ ์ฌ์ฉ์๋ค์๊ฒ๋ ์ ๊ณตํ ์ ์๊ฒ ๋ฉ๋๋ค.
ํค๋ณด๋ ์ง์ ์ธ์๋ ์ฐ๋ฆฌ๋ ํ์ค ๋ธ๋ผ์ฐ์ ํค๋ณด๋ ์ธํฐ๋ ์
๋ฐฉ์์ผ๋ก ํค๋ณด๋ ๋จ์ถํค๋ฅผ ๊ฒ์ฌ ํ์ต๋๋ค. ์ฌ์ฉ์๊ฐ ๋๋๊ทธ ํ์ง ์์ ๊ฒฝ์ฐ ์ฌ์ฉ์๋ ๋ณดํต ํค๋ณด๋๋ฅผ ์ฌ์ฉํฉ๋๋ค. ๋๋๊ทธ ํ๋ ๋์ ์ฐ๋ฆฌ๋ ๋ธ๋ผ์ฐ์ ๋จ์ถํค(tab
๊ณผ ๊ฐ์)๋ฅผ ๋ฌด์ํ๊ณ ๋นํ์ฑํ์ฌ ์ฌ์ฉ์์๊ฒ ๋ถ๋๋ฌ์ด ๊ฒฝํ์ ์ ๊ณตํฉ๋๋ค.
ํ์ฌ๋ ํค๋ณด๋ ์ฒ๋ฆฌ๊ฐ ํ๋ ์ฝ๋ฉ๋์ด ์์ต๋๋ค. ์ด๊ฒ์ ์ถํ์ ์ปค์คํฐ๋ง์ด์งํ ์ ์๋๋ก ์์ ๋ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ด ํ์ฌ์ ํค๋ณด๋ ๋งคํ์ ๋๋ค.:
- tab tab โน - ํ์ค ๋ธ๋ผ์ฐ์ ํญ์
Droppable
๋ค๋น๊ฒ์ดํธ ํฉ๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ์ฌ์ฉ์๊ฐtab
์ ์ ํํ๋ ๋์ ์ด๋ค ํ๋ คํ ๋์๋ ํ์ง ์์ต๋๋ค. ํ๋ฒ ๋๋๊ทธ๋ฅผ ์์ํ๋ฉด ๋๋๊ทธ ์ง์์ ์ํดtab
์ ๋ธ๋ฝ ๋ฉ๋๋ค. - spacebar space - ํฌ์ปค์ค๋
Draggable
์ ๋ค์ด ์ฌ๋ฆฝ๋๋ค. ๋ํspacebar
๋ก ๋๋๊ทธ๊ฐ ์์๋ ๊ณณ์์ ๋๋๊ทธ ํ๋Draggable
์ ๋๋ํฉ๋๋ค. - Up arrow โ - vertical ๋ฆฌ์คํธ์์
Draggable
์ ์๋ก ์ด๋ํฉ๋๋ค. - Down arroa โ - vertical ๋ฆฌ์คํธ์์
Draggable
์ ์๋๋ก ์ด๋ํฉ๋๋ค. - Escape esc - ๋๋๊ทธ ์ค์ธ ๊ฒ์ ์ทจ์ ํฉ๋๋ค. - ์ฌ์ฉ์๊ฐ ํค๋ณด๋ ๋๋ ๋ง์ฐ์ค๋ก ๋๋๊ทธ ํ๊ณ ์๋๊ฒ๊ณผ ๊ด๊ณ ์์ด.
ํ์ฌ ํค๋ณด๋ ๋๋๊ทธ์ ์ ์ ์ฌํญ ์ ๋๋ค: ์ฌ์ฉ์๊ฐ ์๋์ฐ๋ฅผ ์คํฌ๋กคํ๋ฉด ๋๋๊ทธ๊ฐ ์ทจ์ ๋ฉ๋๋ค. ์ด๊ฒ์ ์ผ์ด๋ ์ ์์ต๋๋ค. ์ง๊ธ์ผ๋ก์๋ ๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์ ๊ทผ ๋ฐฉ๋ฒ์ ๋๋ค.
์ ๋๋ฉ์ด์ ์ ํตํด ๋ง์ ๊ฒ์ ์์ง์ผ๋ ์ฌ์ฉ์๋ ์ฐ๋งํด ์ง๋ฉฐ ๋ฐฉํด๊ฐ ๋ฉ๋๋ค. ์ฐ๋ฆฌ๋ ๋ฐธ๋ฐ์ค์ ์ธํฐ๋์ ํผํฌ๋จผ์ค๋ฅผ ๋ณด์ฅํ๊ธฐ ์ํด ๋ง์ ์ ๋๋ฉ์ด์ ์ ์์ ํ์์ต๋๋ค.
๋น์ ์ด ์์ง์ด๋ ์์ดํ
์ ๋๋ํ ๋์ ์์ง์์ ๋ฌผ๋ฆฌํ์ ๊ธฐ์ด๋ก ํ๊ณ ์์ต๋๋ค (thanks react-motion
). ๊ฒฐ๊ณผ์ ์ผ๋ก ๋๋ ๋๋์ด ๋ ๊ฐ์ค๋๊ณ ๋ฌผ๋ฆฌ์ ์ผ๋ก ๋ํ๋ฉ๋๋ค.
๋๋๊ทธ ํ๋ ์์ดํ ์ ์์ง์์ CSS transition์ผ๋ก ํํํ๋ ๊ฒ์ด ๋ฌผ๋ฆฌ์ ์ธ ๊ฒ๋ณด๋ค ์ข์ต๋๋ค. ์ด๊ฒ์ GPU์ผ๋ก ์์ง์์ ํธ๋คํ๋ฉฐ ํผํฌ๋จผ์ค๋ฅผ ๊ทน๋ํํ ์ ์์ต๋๋ค. CSS ์ ๋๋ฉ์ด์ ๊ณก์ ์ ๋ฐฉํด๋ฐ์ง ์๊ณ ๋๋๊ทธ ํ๋ ์์ดํ ์ด ๋ฐ์ผ๋ก ์์ง์ด๋ฉด CSS transition ์ด ๋ฌผ๋ฆฌ์ ์ธ ์ข์ต๋๋ค. ์ด๊ฒ์ GPU๋ก ์์ง์์ ํธ๋คํ๊ฒ ํ๋ฉด์ ํผํฌ๋จผ์ค๋ฅผ ๊ทน๋ํ ํฉ๋๋ค. CSS ์ ๋๋ฉ์ด์ ๊ณก์ ์ ์ปค๋ฎค๋์ผ์ด์ ํ ์ ์๋๋ก ์ค๊ณ ๋์ด ์์ต๋๋ค.
๊ทธ๊ฒ์ ์ด๋ ๊ฒ ๊ตฌ์ฑ๋์์ต๋๋ค:
- ์์ฐ์ค๋ฌ์ด ์๋ต ์๋๋ก ๋ณด์ด๋ ์ค๋น ์๊ฐ
- ๋น ๋ฅด๊ฒ ์์ง์ผ์ ์๋ ์์ ๋จ๊ณ
- ๊ธด ํ๋ฐ๋ถ ๊ทธ๋์ ์ฌ๋๋ค์ ํ๋ฐ๋ถ์๋ ์ ๋๋ฉ์ด์ ๋ ํ ์คํธ๋ฅผ ์ฝ์ ์ ์๋ค
์ ๋๋ฉ์ด์ ๊ณก์ ๋ ์์ง์ผ๋ ์ฌ์ฉ๋๋ค.
# yarn
yarn add react-beautiful-dnd
# npm
npm install react-beautiful-dnd --save
๊ทธ๋์ ์ด๋ป๊ฒ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๊น์?
๋๋๊ทธ ์ค ๋๋กญ์ ์ฌ์ฉํ๊ธฐ ์ํด์ ๋น์ ์ ๋ฉํ๋์ด ๋๋๊ทธ ์ค ๋๋์ ํ ์ ์๋ DragDropContext
React
ํธ๋ฆฌ๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๊ฒ์ DragDropContext
์ผ๋ก ์ ์ฒด ์ ํ๋ฆฌ์ผ์ด์
์ ๋ฉํ๋ ๊ฒ์ด ์ข์ต๋๋ค. ์ค์ฒฉ(nested) DragDropContext
๋ ์ง์๋์ง ์์ต๋๋ค. ๋น์ ์ Droppable
๊ณผ Draggable
props ๋ฅผ ์ฌ์ฉํ์ฌ ์กฐ๊ฑด๋ถ ๋๋๊ทธ ๋๋์ ํ ์ ์์ต๋๋ค. DragDropContext
๋ react-redux Provider component์ ๋น์ทํ ์ฉ๋๋ผ๊ณ ๋ณด์๋ฉด ๋ฉ๋๋ค.
type Hooks = {|
onDragStart?: (id: DraggableId, location: DraggableLocation) => void,
onDragEnd: (result: DropResult) => void,
|}
type Props = Hooks & {|
children?: ReactElement,
|}
import { DragDropContext } from 'react-beautiful-dnd';
class App extends React.Component {
onDragStart = () => {...}
onDragEnd = () => {...}
render() {
return (
<DragDropContext
onDragStart={this.onDragStart}
onDragEnd={this.onDragEnd}
>
<div>Hello world</div>
</DragDropContext>
)
}
}
๋น์ ์ ์คํ ์ดํธ๋ฅผ ์์ ํ ์ ์๋ ๋ง์ ํ ๋ ๋ฒจ ์ดํ๋ฆฌ์ผ์ด์ ์ด๋ฒคํธ๊ฐ ์์ต๋๋ค.
์ด ํจ์๋ ๋๋๊ทธ๋ฅผ ์์ํ ๋ ์๋ฆผ๋ฐ์ ์ ์์ต๋๋ค. ๋น์ ์ ์๋ ์ฌํญ๋ค์ ์ ๊ณต๋ฐ๊ฒ ๋ฉ๋๋ค.
id
: ํ์ฌ ๋๋๊ทธ์ค์ธDraggable
์ id.location
: location์ (droppableId
์index
)Droppable
๋ก ์์๋ ๋๋๊ทธ ์์ดํ ์ ์์น์ ๋๋ค.
๊ทธ๊ฒ์ ๋น์ ์ด ๋๋๊ทธ ํ๋ ๋์ ์ด ํจ์๋ฅผ ์ฌ์ฉํ๋ ๋ชจ๋ Draggable
๊ณผ Droppable
์ปดํฌ๋ํธ์ ์
๋ฐ์ดํธ๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ด highly recommended(๊ฐ๋ ฅํ ์ถ์ฒ) ๋ฉ๋๋ค. (Best hooks
practices ๋ฅผ ๋ณด์ธ์.)
Type information(ํ์ ์ ๋ณด)
onDragStart?: (initial: DragStart) => void
// supporting types(์ง์ํ๋ ํ์
)
type DragStart = {
draggableId: DraggableId,
type: TypeId,
source: DraggableLocation,
}
type DraggableLocation = {|
droppableId: DroppableId,
// the position of the draggable within a droppable(droppable ๋ด์์ droppable ์ ์์น)
index: number
|};
type Id = string;
type DraggableId = Id;
type DroppableId = Id;
type TypeId = Id;
์ด ํจ์๋ ์์ฒญ ์ค์ํ๋ฉฐ ์ ํ๋ฆฌ์ผ์ด์
์ ๋ผ์ดํ์ฌ์ดํด์์ lifecycle ์ํํ ์ญํ ์ ๋ด๋นํ๊ณ ์์ต๋๋ค. ์ด ํจ์๋ ๋ฐ๋์ Draggables
๋ฆฌ์คํธ์ ๋๊ธฐ ๋ฐฉ์์ผ๋ก ์ฌ์ ๋ ฌํด์ผ ํฉ๋๋ค.
๊ทธ๊ฒ์ ๋๋๊ทธ์ ๋ํ ๋ชจ๋ ์ ๋ณด๊ฐ ์ ๊ณต๋ฉ๋๋ค:
result.draggableId
: ๋๋๊ทธ ๋์๋Draggable
์ id.result.type
: ๋๋๊ทธ ๋์๋Draggable
์type
.result.source
:Draggable
์ด ์์๋ ์์น(location).result.destination
:Draggable
์ด ๋๋ ์์น(location). ๋ง์ฝ์Draggable
์ด ์์ํ ์์น์ ๊ฐ์ ์์น๋ก ๋์์ค๋ฉด ์ดdestination
๊ฐ์null
์ด ๋ ๊ฒ์ ๋๋ค.
์๋ํ๋ฉด ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋น์ ์ state๋ฅผ ์ ์ดํ ์ ์๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ณผ
์ ๋ฐ๋ผ ๋๊ธฐ์ ๊ฒฐ๊ณผ๋ฅผ ์ฌ์ ๋ ฌ ํ๋ ๊ฒ์ ๋น์ ์ ๋ฌ๋ ค์์ต๋๋ค.
์ฌ๊ธฐ์ ๋น์ ์ด ์ฌ์ฉํด์ผ ํ๋๊ฒ์ด ์์ต๋๋ค.!
- ๋ง์ฝ
destination
์ดnull
์ด๋ฉด: ๋ชจ๋ ์๋ฃ! - ๋ง์ฝ
source.droppableId
๊ฐdestination.droppableId
์ ๊ฐ์ผ๋ฉด ๋น์ ์ ๋น์ ์ ๋ฆฌ์คํธ์์ ์์ดํ ์ ์ ๊ฑฐํ๊ณ ์ฌ๋ฐ๋ฅธ ์์น์ ์ฝ์ ํด์ผ ํฉ๋๋ค. - ๋ง์ฝ
source.droppableId
๊ฐdestination.droppable
๊ณผ ๋ค๋ฅด๋ฉด ๋น์ ์source.droppableId
๋ฆฌ์คํธ์์Draggable
ํ๊ณ ๊ทธ๊ฒ์destination.droppableId
๋ฆฌ์คํธ์ ์ฌ๋ฐ๋ฅธ ์์น์ ์ถ๊ฐํด์ผ ํฉ๋๋ค.
onDragEnd: (result: DropResult) => void
// supporting types(์ง์ ํ์
)
type DropResult = {|
draggableId: DraggableId,
type: TypeId,
source: DraggableLocation,
// may not have any destination (drag to nowhere)(์ด๋ค destination ๋ ์์ ๊ฒ์
๋๋ค.(์ด๋๋ก๋ ๋๋๊ทธ ๋์ง ์์ต๋๋ค.))
destination: ?DraggableLocation
|}
type Id = string;
type DroppableId = Id;
type DraggableId = Id;
type TypeId = Id;
type DraggableLocation = {|
droppableId: DroppableId,
// the position of the droppable within a droppable(droppable ๋์ droppable์ position)
index: number
|};
Block updates during a drag(๋๋๊ทธ ํ๋ ๋์ ์ ๋ฐ์ดํธ๋ฅผ ๋ธ๋ฝ ํ์ธ์)
์ฌ์ฉ์๊ฐ ๋๋๊น
ํ๋ ๋์ Draggable
๊ณผ Droppable
ํน์ ์น์์ ๊ฒฐ๊ณผ์ ์ํฅ์ ์ค ์ ์๋ ๋ชจ๋ ์
๋ฐ์ดํธ๋ฅผ ์ฐจ๋จํ๋ ๊ฒ์ ๊ฐ๋ ฅํ ์ถ์ฒํฉ๋๋ค. onDragStart
์์ onDragEnd
๊น์ง Draggable
๊ณผ Droppable
์ ์
๋ฐ์ดํธ๋ฅผ ์ฐจ๋จํด ์ฃผ์ธ์.
์ฌ์ฉ์๊ฐ ๋๋๊น
์ ์์ํ ๋ ์ฐ๋ฆฌ๋ ์ ํ๋ฆฌ์ผ์ด์
์ Draggable
๊ณผ Droppable
๋
ธ๋ ์ฐจ์์ ๋ชจ๋ ์ค๋
์ท์ ์ป์ ์ ์์ต๋๋ค. ๋ง์ฝ ๋๋๊ทธ ํ๋ ๋์ ๊ทธ๊ฒ๋ค์ด ๋ณ๊ฒฝ๋๋ฉด ์ฐ๋ฆฌ๋ ๊ทธ๊ฒ์ ๋ํด ๋ชจ๋ฅผ ๊ฒ ์
๋๋ค.
์ฌ๊ธฐ์ ๋น์ ์ด ๋๋๊ทธ ํ๋ ๋์ ๋ณ๊ฒฝ์ ํด์ ๋ฐ์ํ ์ ์๋ ๋ช ๊ฐ์ง ๋ถ์กฑํ ์ฌ์ฉ์ ๊ฒฝํ์ด ์์ต๋๋ค:
- ๋น์ ์ด ๋ ธ๋์ ์๋ฅผ ๋๋ฆฌ๋ฉด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ทธ๊ฒ์ ๋ํด ์์ง ๋ชปํ๊ฒ ๋๋ฉฐ ์ฌ์ฉ์๊ฐ ์์ํ๋๋ฐ๋ก ๋์ํ์ง ์๊ฒ ๋ฉ๋๋ค.
- ๋น์ ์ด ๋ ธ๋์ ์๋ฅผ ์ค์ด๋ฉด ๋น์ ์ ๋ฆฌ์คํธ์์ ์์ํ์ง ๋ชปํ ์์ง์๊ณผ ์ฐจ์ด๊ฐ ์๊ธฐ๊ฒ ๋ฉ๋๋ค.
- ๋น์ ์ด ๋ ธ๋์ ํฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ๊ทธ๊ฒ์ ๋ณ๊ฒฝ๋ ๋ ธ๋์ ๋ค๋ฅธ ๋ ธ๋ ๋ชจ๋ ์๋ชป๋ ์๊ฐ์ ์ด๋ํ๊ฒ ๋ฉ๋๋ค.
- ๋น์ ์ด ๋๋๊ทธ ๋ ธ๋์ ํฌ๊ธฐ๋ฅผ ๋ณ๊ฒฝํ๊ฒ ๋๋ฉด ์ ํํ ์๊ฐ์ ๋ค๋ฅธ ๋ ธ๋๋ค์ด ์์ง์ด์ง ์๊ฒ ๋ฉ๋๋ค.
onDragStart
and onDragEnd
pairing(์)
์ฐ๋ฆฌ๋ onDragStart
์ด๋ฒคํธ๊ฐ ๋จ์ผ onDragEnd
์ด๋ฒคํธ์ ์์ ์ด๋ฃจ๋ ๊ฒ์ ๋ณด์ฅํฉ๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ด ํ๋ฆฐ ๊ฒฝ์ฐ๊ฐ ์์ต๋๋ค. - ๊ทธ๊ฒ์ ๋ฒ๊ทธ ์
๋๋ค. ํ์ฌ ์ธ๋ถ์์ ๋๋๊ทธ๋ฅผ ์ทจ์ํ ๋ฉ์ปค๋์ฆ์ด ์์ต๋๋ค..
Style(์คํ์ผ)
๋๋๊ทธ ์ค์ ๋๊ฐ์ง ์คํ์ผ์ ๋ฐ๋(body)์ ์ถ๊ฐํ๋์๊ฒ์ ์ถ์ฒ ํฉ๋๋ค.
user-select: none;
๊ทธ๋ฆฌ๊ณcursor: grab;
(ํน์ ๋๋๊ทธ ์ค์ ๋น์ ์ด ์ํ๋ ์ปค์(cursor))
user-select: none;
ํ
์คํธ๋ฅผ ๋๋๊ทธํ์ง ๋ชปํ๊ฒ ํฉ๋๋ค.
cursor: [your desired cursor];
์ ํ์ํฉ๋๋ค. ์๋ํ๋ฉด pointer-events: none;
์ด ๋๋์ด ์์ดํ
์ ์ ์ฉ๋๊ธฐ ๋๋ฌธ์ด๋ค. ์ด๋ ๊ฒ ๋๋ฉด snapshot.isDragging
์ ๊ธฐ๋ฐ์ผ๋ก ๋๋๊ทธ์ ๋น์ ์ ์ปค์๋ฅผ ์ค์ ํ์ง ๋ชปํ๊ฒ ๋๋ค. (see Draggable
).
Dynamic hooks
๋น์ ์ hook ํจ์๋ ์ค์ง ์์ํ ๋๋ง ์บก์ฒ ๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ ๊ทธ ํ์ ์ด ํจ์๋ฅผ ๋ณ๊ฒฝํ๋ฉด ์๋ฉ๋๋ค. ๋ง์ฝ ํฉ๋นํ ๊ฒฝ์ฐ๊ฐ ์๊ฒ๋๋ฉด ํ ์ ์ง์ํ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ์ง๊ธ์ ์๋๋๋ค.
Droppable
์ปดํฌ๋ํธ๋ Draggable
์ ์ํด **๋๋(dropped)**๋ ์ ์์ต๋๋ค. ๋ํ Draggable
์ ํฌํจ ํฉ๋๋ค.. Draggable
์ ๋ฐ๋์ Droppable
์์ ํฌํจ๋์ด์ผ ํฉ๋๋ค.
import { Droppable } from 'react-beautiful-dnd';
<Droppable
droppableId="droppable-1"
type="PERSON"
>
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey'}}
>
I am a droppable!
</div>
)}
</Droppable>
droppableId
: ํ์DroppableId(string)
, ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํ ๋๋ ๊ฐ๋ฅ ์ฌ๋ถ๋ฅผ ์๋ณํ๋ ์ ๋ํฌ ์๋ณ์ ์ ๋๋ค. ์ด prop๋ ์์ ํ์ง ๋ง์ธ์ - ํนํ ๋๋๊ทธ ์คtype
: ์ต์ TypeId(string)
,Draggable
ํด๋์ค๋ฅผ ๋ฐ๊ธฐ ์๊ธฐ ์ฌ์ฉ ๋ฉ๋๋ค. ์๋ฅผ ๋ค์ด,PERSON
์ ์ฌ์ฉํ๋ฉดPERSON
ํ์ ์Draggable
๋ง ๋๋๋ ์ ์์ต๋๋ค.TASK
ํ์Draggable
์PERSON
Droppable
์ ๋๋๋ ์ ์์ต๋๋ค. ๋ง์ฝtype
์ด ์ ๊ณต๋์ง ์์ผ๋ฉด ๊ทธ๊ฒ์DEFAULT
๋ก ์ค์ ๋ฉ๋๋ค. ํ์ฌDroppable
์ค์Draggable
์ ์type`์ ๋ฐ๋์ ๊ฐ์์ผ ํฉ๋๋ค. ๋ง์ฝ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด ์ด ์ ํ์ ๋์จํด์ง ์ ์์ต๋๋ค.isDropDisabled
: ์ต์ ,Droppable
์ ๋๋์ด ํ์ฉ๋๋์ง ์ ์ดํ๋ ํ๋๊ทธ ์ ๋๋ค. ๋น์ ์ ์กฐ๊ฑด๋ถ ๋๋ ๋ก์ง์ ๊ตฌํํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ์false
์ ๋๋ค.
Droppable
์ React
์์๋ค์ ๋ฐ๋์ ReactElement
๋ฅผ ๋ฐํํ๋ ํจ์์ฌ์ผ ํฉ๋๋ค.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
// ...
)}
</Droppable>
์ด ํจ์๋ ๋ argument๋ฅผ ์ ๊ณตํฉ๋๋ค.
1. provided: (Provided)
type Provided = {|
innerRef: (HTMLElement) => void,
|}
droppable์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ค๋ฉด, ๋น์ ์ ๋ฐ๋์ provided.innerRef
๋ฅผ ReactElement
์ ์ต์๋จ DOM ๋
ธ๋์ ๋ฐ์ธ๋(bind)ํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ๋ ReactDOM
๋ฅผ ์ฌ์ฉํ์ง ์๊ธฐ ์ํด ๋น์ ์ DOM ๋
ธ๋๋ฅผ ์ฐพ์ ๊ฒ์
๋๋ค.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div ref={provided.innerRef}>
Good to go
</div>
)}
</Droppable>
2. snapshot: (StateSnapshot)
type StateSnapshot = {|
isDraggingOver: boolean,
|}
๋ํ children
ํจ์๋ ํ์ฌ ๋๋๊ทธ state์ ๊ด๋ จ๋ ๋ช ๊ฐ์ง state๋ฅผ ์ ๊ณต ํฉ๋๋ค. ์ด๊ฒ์ ๋น์ ์ ์ปดํฌ๋ํธ๋ฅผ ํฅ์์ํค๊ธฐ ์ํด ์ฌ์ฉ๋ ์ ์์ต๋๋ค(์ต์
). ์ผ๋ฐ์ ์ผ๋ก ์์ ํ๋ ๊ฒฝ์ฐ๋ ๋๋๊ทธ ์ค์ Droppable
๋ชจ์์ ๋ณ๊ฒฝํ๊ธฐ ์ํด ์ฌ์ฉ ํฉ๋๋ค.
<Droppable droppableId="droppable-1">
{(provided, snapshot) => (
<div
ref={provided.innerRef}
style={{backgroundColor: snapshot.isDraggingOver ? 'blue' : 'grey'}}
>
I am a droppable!
</div>
)}
</Droppable>
์ ์ ํ์ธ์. ์ด๋ฒ์ ์ด๊ฒ์ ์ง์๋์ง ์์ต๋๋ค. ํ์ฌ ์ด๊ธฐ ๋ฒ์ ์์๋ ์ค์ง ๋จ์ผ ๋ฆฌ์คํธ์ ์ฌ์ ๋ ฌ๋ง ์ง์๋ฉ๋๋ค.
Droppable
์ ์ค์ง ๊ฐ์type
์ ๊ณต์ ํ๋Draggable
๋ก๋ง ๋๋๋ ์ ์์ต๋๋ค. ์ด๊ฒ์ ์กฐ๊ฑด๋ถ ๋๋์ ํ์ฉํ๋ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๋๋ค. ๋ง์ฝ ๋น์ ์ดDraggable
์ ์ํtype
์ ์ ๊ณตํ์ง ์๋๋ค๋ฉด ๊ทธ๊ฒ์ default type์ ๊ฐ์งDraggable
๋ง ํ์ฉํ ๊ฒ์ ๋๋ค.Draggable
๊ณผDroppable
์ ์ด๋คtype
๋ ์ ๊ณตํ์ง ์์ ๊ฒฝ์ฐDEFAULT
๋ก ์ค์ ๋ฉ๋๋ค. ํ์ฌ ๋ค์์ 'type' ํน์Draggable
์ ์์ผ๋์นด๋(wildcard)type
์ ์ค์ ํ๋ ๋ฐฉ๋ฒ์ ์์ต๋๋ค. ์ด๊ฒ์ ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด ์ถ๊ฐ๋ ๊ฒ์ ๋๋ค.isDropDisabled
prop ์ ์ฌ์ฉํด์ ์กฐ๊ฑด์ ์ผ๋ก ๋๋์ ํ์ฉํ ์ ์์ต๋๋ค. ์ด๊ฒ์ ๋น์ ์์๋ก ์กฐ๊ฑด ์ํ์ ํ ์ ์๋๊ฒ์ ํ์ฉํฉ๋๋ค. ์ด๊ฒ์Droppable
์type
์ด ํ์ฌ ๋๋๋๊ณ ์๋Draggable
์type
๊ณผ ๊ฐ์ ๊ฒฝ์ฐ ๊ณ ๋ คํ ์ ์์ต๋๋ค.isDropDisabled
๋ฅผ false๋ก ์ค์ ํ๋ฉด์Droppable
์ ๋๋๋๋ ๊ฒ์ ๋ง์ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ์์ฑํ ๋ฆฌ์คํธ์ ์ ๋ ๋๋ํ ์ ์์ง๋งDraggable
์ ํฌํจํ ์ ์์ต๋๋ค.- ๊ธฐ์ ์ ์ผ๋ก ๋น์ ์
type
์ ์ฌ์ฉํ ํ์ ์์ผ๋ฉฐisDropDisabled
ํจ์๋ก ๋ชจ๋ ์กฐ๊ฑด๋ถ ๋๋ ๋ก์ง์ ์ํํ ์ ์์ต๋๋ค.type
ํ๋ผ๋ฏธํฐ๋ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ์ ๊ฐ๋จํ ๋ฐฉ๋ฒ์ ๋๋ค.
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ scroll container ๋ด์์์ ๋๋๊ทธ๋ฅผ ์ง์ ํฉ๋๋ค. (DOM ์๋ฆฌ๋จผํธ๋ overflow: auto;
ํน์ overflow: scroll;
๋ฅผ ๊ฐ์ง๋๋ค.) ์ ์ผ ํ๊ฒ ์ง์ ๋๋ ๊ฒฝ์ฐ๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค:
Droppable
์์ฒด๊ฐ ๋ถ๋ชจ ์คํฌ๋กค์ด ์๋ scroll container์ผ ์ ์์ต๋๋ค.Droppable
์ด ํ๋์ ์คํฌ๋กคํ ์ ์๋ ๋ถ๋ชจ๋ฅผ ๊ฐ์ง๊ณ ์์ต๋๋ค.*
Auto scrolling is not provided(auto scroll์ ์ ๊ณต๋์ง ์์ต๋๋ค)
ํ์ฌ scroll container์ auto scroll์ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์์ญ์ด ์๋๋๋ค. auto scroll์ container๊ฐ scroll container์ ๋ ๊ทผ์ฒ๋ก ๋๋๊ทธ ํ ๋ ์ค์ค๋ก๊ฐ ์์ดํ
๋๋๊ทธ๋ฅผ ์ํ ๊ณต๊ฐ์ ๋ง๋ญ๋๋ค. ๋น์ ์ auto scroll ๋ฆฌ์คํธ๋ฅผ ๋ง๋ค๊ฑฐ๋ ๋ง์ฝ ๋น์ ์ด ํ์ํ๋ฉด ์ฐ๋ฆฌ๋ auto scroll Droppable
์ ์ ๊ณตํ ์ ์์ต๋๋ค.
์ฌ์ฉ์๋ค์ ๊ทธ๋ค์ ํธ๋ํจ๋ ํน์ ๋ง์ฐ์ค ํ ๋ก ๋๋๊ทธ ์ค์ scroll container๋ฅผ ์คํฌ๋กค ํ ์ ์์ต๋๋ค.
Keyboard dragging limitation(ํค๋ณด๋ ๋๋๊ทธ ์ ํ)
scroll container๋ก ์์ ํ๊ธฐ ์ํด ํค๋ณด๋ ๋๋๊ทธ๋ฅผ ๊ฐ์ ธ์ค๋ ๊ฒ์ ์๋นํ ์ด๋ ต ์ต๋๋ค. ํ์ฌ ์ฌ๊ธฐ์๋ ์ ํ์ด ์์ต๋๋ค. ๋น์ ์ ํค๋ณด๋๋ก scroll container ๋ชจ์๋ฆฌ๋ฅผ ๋์ด์ ๋๋๊ทธ๋ฅผ ํ ์ ์์ต๋๋ค. ์ด ์ ํ์ autu scrolling ์ด ์๊ฐ๋๋ฉด ์์ด์ง ๊ฒ์ ๋๋ค.
Draggable
์ปดํฌ๋ํธ๋ Droppable
์ ๋๋๊ทธ ๋ฐ ๋๋๊ทธ ํ ์ ์์ต๋๋ค. Draggable
์ ๋ฐ๋์ Droppable
์ ํฌํจํด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ๊ฐ๋ฅํ ๊ทธ๊ฒ์ Droppable
์ด ๋ค๋ฅธ Droppable
๋ก ์์ง์ด๋ ์ค์ Draggable
์ ์ฌ์ ๋ ฌ ํ ๊ฒ์
๋๋ค. ๊ทธ๊ฒ์ ๊ฐ๋ฅํ ์
๋๋ค. ์๋ํ๋ฉด Droppable
์ ๊ทธ๊ฒ์ ๋๋๋๋ ๊ฒ์ ์ปจํธ๋กค ํ๋ ๊ฒ์์ ์์ ๋กญ๊ธฐ ๋๋ฌธ์
๋๋ค.
์ฃผ์:
Droppable
๋ค ์ฌ์ด์์ ์ด๋ํ๋ ๊ฒ์ ์ด ์ด๊ธฐ๋ฒ์ ์์ ์ง์ํ์ง ์์ต๋๋ค.
import { Draggable } from 'react-beautiful-dnd';
<Draggable
draggableId="draggable-1"
type="PERSON"
>
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
>
<h4>My draggable</h4>
</div>
{provided.placeholder}
</div>
)}
</Draggable>
์ฃผ์: ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ Reac๊ฐ 16 ๋ฒ์ ์ด ๋ ๊ฒฝ์ฐ ๋ฉํ ์๋ฆฌ๋จผํธ๋ฅผ ๋ง๋ค ํ์ ์์ด ๋น์ ์ ์์ ํจ์์ sibling์ผ๋ก placeholder`๋ฅผ ๋ฐํํ ์ ์๊ธฐ ๋๋ฌธ์ ์กฐ๊ธ ์ ๋ฆฌ๋ ์ ์์ ๊ฒ์ ๋๋ค.
draggableId
: ํ์DraggableId(string)
, ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํDraggable
์ ๊ณ ์ ํ๊ฒ ์๋ณํฉ๋๋ค. ์ด prop๋ฅผ ์์ ํ์ง ๋ง์ธ์. - ํนํ ๋๋๊ทธ ์ค์.type
: ์ต์ Draggable
์ type (TypeId(string)
), ์ด๊ฒ์Droppable
์Draggable
์ด ๋๋์ ํ์ฉํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค.Draggable
์ ์ค์งDroppable
๊ณผ ๊ฐ์type
์ ๊ณต์ ํ ๊ฒฝ์ฐ ๋๋๋ฉ๋๋ค. ๋ง์ฝtype
์ด ์ ๊ณต๋์ง ์์ผ๋ฉด ๊ธฐ๋ณธ์ผ๋ก'DEFAULT'
๋ก ์ค์ ๋ฉ๋๋ค. ํ์ฌDraggable
์type
์ ๋ฐ๋์ ๊ทธ๊ฒ์Droppable
container์type
๊ณผ ๊ฐ์์ผ ํฉ๋๋ค. ์ด ์ ํ์ ์ถํ ์ฌ์ฉ์ด ํ์ํ ๊ฒฝ์ฐ๊ฐ ์๊ธฐ๋ฉด ๋์จํด์ง ์ ์์ต๋๋ค.isDragDisabled
: ์ต์ Draggable
์ด ๋๋๊ทธ๋ฅผ ํ์ฉํ ์ ์๋์ง ์ฌ๋ถ๋ฅผ ์ ์ดํ๋ ํ๋๊ทธ ์ ๋๋ค. ๋น์ ์ ๋๋๊ทธ ์ ์ด ๋ก์ง์ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ๊ฐ์false
์ ๋๋ค.
Draggable
์ React
์์๋ค์ ๋ฐ๋์ ReactElement
๋ฅผ ๋ฐํํ๋ ํจ์์ฌ์ผ ํฉ๋๋ค.
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
>
Drag me!
</div>
{provided.placeholder}
</div>
)}
</Draggable>
์ด ํจ์๋ ๋ argument๋ฅผ ์ ๊ณตํฉ๋๋ค:
1. provided: (Provided)
type Provided = {|
innerRef: (HTMLElement) => void,
draggableStyle: ?DraggableStyle,
dragHandleProps: ?DragHandleProvided,
placeholder: ?ReactElement,
|}
๋ชจ๋ ์ ๊ณต๋ ์ค๋ธ์ ํธ๋ Draggable
์ด ์ฌ๋ฐ๋ฅด๊ฒ ์ ์ฉ๋์ด์ผ ํฉ๋๋ค.
provided.innerRef (innerRef: (HTMLElement) => void)
:Droppable
์ด ์ฌ๋ฐ๋ฅด๊ฒ ์๋ํ๋ ค๋ฉด, ๋น์ ์ ๋ฐ๋์innerRef
ํจ์๋ฅผ ๋น์ ์ด ์ํ๋Draggable
๋ ธ๋์ReactElement
์ ๋ฐ์ธ๋ฉ ํด์ผ ํฉ๋๋ค. ์ฐ๋ฆฌ๋ReactDOM
๋ฅผ ์ฌ์ฉํ ํ์ ์๋๋ก ํ๊ธฐ ์ํด ๋น์ ์ DOM ๋ ธ๋๋ฅผ ์ฐพ์ ๊ฒ์ ๋๋ค.
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div ref={provided.innerRef}>
Drag me!
</div>
)}
</Draggable>
Type information(ํ์ ์ ๋ณด)
innerRef: (HTMLElement) => void
provided.draggableStyle (?DraggableStyle)
: ์ด๊ฒ์Object
ํน์null
์ ๋๋ค. ์ด๊ฒ์Draggable
์ ์ฉ๋ ํ์๊ฐ ์๋ ๋ค ์์ ์คํ์ผ์ ํฌํจํฉ๋๋ค. ์ด๊ฒ์provided.innerRef
๋ฅผ ์ ์ฉํ ๋์ผํ ๋ ธ๋์ ์ ์ฉํด์ผ ํฉ๋๋ค. ์ด๊ฒ์ ๊ทธ๊ฒ์ด ๋๋๊ทธ ์ค์ด๋ ๋๋๊ทธ ์ค์ด์ง ์๊ฑฐ๋ draggable์ ์์ง์์ ์ ์ดํฉ๋๋ค. ๋น์ ์ ์คํ์ผ์ ์ด ์ค๋ธ์ ํธ์ ์ถ๊ฐํ์ธ์. - ๊ทธ๋ฌ๋ ์ด๋ค properties๋ ์ญ์ ํ๊ฑฐ๋ ์์ ํ์ง ๋ง์ธ์.
Ownership(์์ ๊ถ)
๋๋๊ทธ ์๋ฆฌ๋จผํธ์ ์์น ๋ก์ง์ ์ป๋ ๊ฒ์ ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ช
์ธ๋์ด ์์ต๋๋ค. ์ด๊ฒ์ top
, right
, bottom
, left
๊ทธ๋ฆฌ๊ณ transform
๊ณผ ๊ฐ์ ํ๋กํผํฐ๋ฅผ ํฌํจํฉ๋๋ค. ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๊ทธ๊ฒ์ ์์น๋ฅผ ๋ณ๊ฒฝํ ์ ์์ผ๋ฉฐ ๊ทธ๊ฒ์ ์ฃผ์ ๋ฒ์ ์ ํผํฌ๋จผ์ค ๋ฌธ์ ์์ด ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ทธ๊ฒ์ ์ญ์ ๋๋๊ทธ ์๋ฆฌ๋จผํธ์ transition
ํ๋กํผํฐ๋ฅผ ์ ์ฉํ์ง ์์ ๊ฒ์ ์ถ์ฒํฉ๋๋ค.
Warning: position: fixed
react-beautiful-dnd
๋ ๋๋๊ทธ ์๋ฆฌ๋จผํธ์ ํฌ์ง์
์ position: fixed
๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ด๊ฒ์ ํ์คํ ํํํ๋ฉฐ ๋น์ ์ด position: relative | absolute | fixed
๋ถ๋ชจ๋ฅผ ๊ฐ์ง๋ ๊ฒ์ ํ์ฉ ํฉ๋๋ค. ๊ทธ๋ฌ๋ ๋ถํดํ๊ฒ๋ position:fixed
๋ transform
์ ์ํด ์ํฅ ๋ฐ์ต๋๋ค (transform: rotate(10deg);
์ ๊ฐ์ด). ์ด ์๋ฏธ๋ ๋น์ ์ด ๋ง์ฝ Draggable
์ ๋ถ๋ชจ ์ค ํ๋๊ฐ transform: *
๋ฅผ ๊ฐ์ง๋ค๋ฉด ๋๋๊ทธ ์ค์ ํฌ์ง์
๋ก์ง์ ๋ง์ง ์์ ๊ฒ์
๋๋ค. ๋ง์ ์ฌ์ฉ์๋ฅผ ์ํด ์ด๊ฒ์ ์ด์์
๋๋ค. ๊ทธ๋๋ ๊ทธ๊ฒ์ ์ ์๋ฆฌ์ ๋๊ธฐ ๋ณด๋ค๋ ํฌํธ ์๋ฃจ์
์ ๋๋๊ทธ ์๋ฆฌ๋จผํธ๋ฅผ ๋ถ์ด๋ ๊ฒ์ด ๋ ๋ซ์ต๋๋ค. ๊ทธ๋ฌ๋ ์ ์๋ฆฌ์ ๋๋๊ฒ์ ๋ชจ๋๋ฅผ ์ํด์ ์ข์ ๊ฒฝํ(experience)์
๋๋ค. ์ง๊ธ ์ฐ๋ฆฌ๋ ๊ทธ๊ฒ ๊ทธ๋๋ก ๋ ๊ฒ์
๋๋ค. ๊ทธ๋ฌ๋ ์ด๊ฒ์ด ๋น์ ์๊ฒ ์ค์ํ๋ค๋ฉด ๋ง์๋๊ณ ์ด์๋ฅผ ์ ๊ธฐํด ์ฃผ์ธ์.
Usage of draggableStyle
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
>
Drag me!
</div>
</div>
)}
</Draggable>
Extending with your own styles(๋น์ ์ ์คํ์ผ์ ํ์ฅ)
<Draggable draggable="draggable-1">
{(provided, snapshot) => {
const style = {
...provided.draggableStyle,
backgroundColor: snapshot.isDragging : 'blue' : 'white',
fontSize: 18,
}
return (
<div>
<div
ref={provided.innerRef}
style={style}
>
Drag me!
</div>
</div>
);
}}
</Draggable>
Type information(ํ์ ์ ๋ณด)
type DraggableStyle = DraggingStyle | NotDraggingStyle;
type DraggingStyle = {|
position: 'fixed',
boxSizing: 'border-box',
// allow scrolling of the element behind the dragging element
pointerEvents: 'none',
zIndex: ZIndex,
width: number,
height: number,
top: number,
left: number,
transform: ?string,
|}
type NotDraggingStyle = {|
transition: ?string,
transform: ?string,
pointerEvents: 'none' | 'auto',
|}
provided.placeholder (?ReactElement)
TheDraggable
element hasposition: fixed
applied to it while it is dragging. The role of theplaceholder
is to sit in the place that theDraggable
was during a drag. It is needed to stop theDroppable
list from collapsing when you drag. It is advised to render it as a sibling to theDraggable
node. When the library moves toReact
16 theplaceholder
will be removed from api.
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
>
Drag me!
</div>
{/* Always render me - I will be null if not required */}
{provided.placeholder}
</div>
)}
</Draggable>
provided.dragHandleProps (?DragHandleProps)
๋ชจ๋Draggable
์ drag handle์ ๊ฐ์ง๋๋ค. ์ด๊ฒ์ ์ ์ฒดDraggable
์ ๋๋๊ทธ ํ๊ธฐ ์ํด ์ฌ์ฉ๋ฉ๋๋ค. ๊ฐ๋ ์ด๊ฒ์Draggable
๊ณผ ๊ฐ์ ๋ ธ๋์ผ ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋๋ก ๊ทธ๊ฒ์Draggable
์ ์์์ด ๋ ์ ์์ต๋๋ค.DragHandleProps
๋ ๋น์ ์ด ๋๋๊ทธ ํธ๋ค์ด ๋๊ณ ์ ํ๋ ๋ ธ๋์ ์ ์ฉ๋ ํ์๊ฐ ์์ต๋๋ค. ์ด๊ฒ์Draggable
๋ ธ๋์ ์ ์ฉ๋ ํ์๊ฐ ์๋ ๋ค์์ prop ์ ๋๋ค. ์ด๊ฒ์ ๊ฐ๋จํ๊ฒ draggable node์ ๋ฟ๋ฆด(spread) ์ ์์ต๋๋ค. ({...provided.dragHandleProps}
). ๊ทธ๋ฌ๋ ๋ง์ฝ ๋น์ ์ด ๊ทธ๊ฒ๋ค์ ์๋ต์ด ํ์ํ๋ฉด ๋น์ ์ ์ญ์ monkey patch์ prop๋ฅผ ํ์ํ ๊ฒ์ ๋๋ค. DragHandleProps ์ญ์isDragDisabled
๊ฐtrue
๋ก ์ค์ ๋ ๊ฒฝ์ฐnull
์ผ ๊ฒ์ ๋๋ค.
Type information(ํ์ ์ ๋ณด)
type DragHandleProps = {|
onMouseDown: (event: MouseEvent) => void,
onKeyDown: (event: KeyboardEvent) => void,
onClick: (event: MouseEvent) => void,
tabIndex: number,
'aria-grabbed': boolean,
draggable: boolean,
onDragStart: () => void,
onDrop: () => void
|}
Standard example(๊ธฐ๋ณธ ์์ )
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
>
Drag me!
</div>
{provided.placeholder}
</div>
)}
</Draggable>
Custom drag handle
<Draggable draggableId="draggable-1">
{(provided, snapshot) => (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
>
<h2>Hello there</h2>
<div {...provided.dragHandleProps}>
Drag handle
</div>
</div>
{provided.placeholder}
</div>
)}
</Draggable>
Monkey patching
๋ง์ฝ ๋น์ ์ด
DragHandleProps
์ prop์ค ํ๋๊ฐ ํ์ํ๋ค๋ฉด
const myOnClick = (event) => console.log('clicked on', event.target);
<Draggable draggableId="draggable-1">
{(provided, snapshot) => {
const onClick = (() => {
// dragHandleProps might be null(dragHandleProps ๋ null ์ผ ๊ฒ์
๋๋ค.)
if(!provided.dragHandleProps) {
return myOnClick;
}
// creating a new onClick function that calls my onClick(my onClick์ ํธ์ถํ๋ ์๋ก์ด onClick ํจ์ ์์ฑ)
// event as well as the provided one.(ํ๋๋ก ์ ๊ณต๋ event)
return (event) => {
provided.dragHandleProps.onClick(event);
// You may want to check if event.defaultPrevented(event.defaultPrevented๊ฐ true์ธ์ง ํ์ธํ๊ณ )
// is true and optionally fire your handler(์ ํ์ ์ผ๋ก ํธ๋ค๋ฌ๋ฅผ ์์ํ๋ ๊ฒ์ด ์ข์ต๋๋ค.)
myOnClick(event);
}
})();
return (
<div>
<div
ref={provided.innerRef}
style={provided.draggableStyle}
{...provided.dragHandleProps}
onClick={onClick}
>
Drag me!
</div>
{provided.placeholder}
</div>
);
}}
</Draggable>
2. snapshot: (StateSnapshot)
type StateSnapshot = {|
isDragging: boolean,
|}
children
ํจ์ ์ญ์ ํ์ฌ ๋๋๊ทธ state์ ๊ด๋ จ๋ ๋ช๊ฐ์ง ์์ state์ ํจ๊ป ์ ๊ณต๋๋ค. ์ด๊ฒ์ ์ ํ์ ์ผ๋ก ๋น์ ์ ์ปดํฌ๋ํธ๋ฅผ ํฅ์์ํจ๋ค. ์ผ๋ฐ์ ์ธ ์ํฉ์ ๋๋๊ทธ๊ฐ ๋๋ ๊ฒฝ์ฐ Draggable
๊ฐ ๋ณด์ฌ์ง๋ ๊ฒ์ ๋ณ๊ฒฝํ๋ ๊ฒ์ด๋ค. ์ฃผ์: ๋ง์ฝ ๋น์ ์ด ์ปค์(cursor)๋ฅผ grab
๊ณผ ๊ฐ์ ๊ฒ์ผ๋ก ๋ณ๊ฒฝํ๊ณ ์ถ๋ค๋ฉด ๋น์ ์ style์ body์ ์ถ๊ฐํ ํ์๊ฐ ์๋ค. (์ฐธ๊ณ DragDropContext` > style above)
<Draggable draggableId="draggable-1">
{(provided, snapshot) => {
const style = {
...provided.draggableStyle,
backgroundColor: snapshot.isDragging ? 'blue' : 'grey',
};
return (
<div>
<div
ref={provided.innerRef}
style={style}
{...provided.dragHandleProps}
>
Drag me!
</div>
{provided.placeholder}
</div>
);
}}
</Draggable>
์ด ์ฝ๋ ๋ฒ ์ด์ค๋ flowtype๋ฅผ ์ฌ์ฉํ์ฌ ๋ด๋ถ ์ผ๊ด์ฑ์ ๋์ด๊ณ ํ๋ ฅ์ ์ธ ์ฝ๋๋ฅผ ๋ง๋ญ๋๋ค.
์ด ์ฝ๋ ๋ฒ ์ด์ค๋ ์ ๋(unit), ํผํฌ๋จผ์ค(performance) ๊ทธ๋ฆฌ๊ณ ํตํฉ(integration) ํ ์คํธ๋ฅผ ํฌํจํ์ฌ ๋ช๊ฐ์ง ๋ค๋ฅธ ํ ์คํธ ์ ๋ต์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ํ ์คํธ๋ ์์คํ ์ ๋ค์ํ ์ ์์ ํ๋ฆฌํฐ์ ์์ ์ฑ์ ๋์ด๋ ๊ฒ์ ๋์์ค๋๋ค.
์ฝ๋ ์ปค๋ฒ๋ฆฌ์ง๋ not a guarantee of code health ์ ๋๋ค. ๊ทธ๊ฒ์ ์ข์ ์งํ์ ๋๋ค. ์ด ์ฝ๋ ๋ฒ ์ด์ค๋ ๊ธฐ์กด ์ฌ์ดํธ์ ~95%๋ฅผ ์ปค๋ฒ๋ฆฌ์ง ํฉ๋๋ค.
์ด ์ฝ๋๋ฒ ์ด์ค๋ ๋งค์ ๋ฐ์ด๋ ํผํฌ๋จผ์ค๋ฅผ ๋ฐํํ๋๋ก ์ค๊ณ๋์ด ์์ต๋๋ค - ๊ทธ๊ฒ์ DNA ์ค ํ๋์
๋๋ค. ์ด๊ฒ์ React
ํผํฌ๋จผ์ค ์ฑ๋ฅ ์กฐ์ฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํฉ๋๋ค. ์ฌ๊ธฐ ๊ทธ๋ฆฌ๊ณ ์ฌ๊ธฐ. ์ด๊ฒ์ ๊ฐ ์์
์ ํ์ํ ์ต์ ๋ ๋๋ง ์๋ฅผ ์ํํ๋๋ก ์ค๊ณ๋์์ต๋๋ค.
Highlights(ํ์ด๋ผ์ดํธ)
- using connected-components with memoization to ensure the only components that render are the ones that need to - thanks
react-redux
,reselect
andmemoize-one
- all movements are throttled with a
requestAnimationFrame
- thanksraf-schd
- memoization is used all over the place - thanks
memoize-one
- conditionally disabling
pointer-events
onDraggable
s while dragging to prevent the browser needing to do redundant work - you can read more about the technique here - Non primary animations are done on the GPU
Minimal browser paints | Minimal React updates |
---|---|
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ๋ฐ์คํฌํ์ฉ ํ์ค Atlassian ์ง์ ๋ธ๋ผ์ฐ์ ์ ์ง์ํฉ๋๋ค:
Desktop | Version |
---|---|
Microsoft Internet Explorer(Windows) | Version 11 |
Microsoft Edge | Latest stable version supported |
Mozilla Firefox (all platforms) | Latest stable version supported |
Google Chrome (Windows and Mac) | Latest stable version supported |
Safari (Mac) | Latest stable version on latest OS release supported |
ํ์ฌ ๋ชจ๋ฐ์ผ์ ์ง์๋์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํฐ์น ์ง์ํ ๊ณํ์ ๊ฐ์ง๊ณ ์์ต๋๋ค.
์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฌธ์๋ ๋ค๋ฅธ ์ธ์ด๋ก๋ ์ ๊ณต๋ฉ๋๋ค.
- Korean: leehyunggeun/react-beautiful-dnd
์ด ๋ฒ์ญ๋ณธ๋ค์ ์ปค๋ฎค๋ํฐ์ ์ํด ์ด์๋๊ณ ์์ผ๋ฉฐ ๋ฉ์ธํฐ๋ค์ด์ด์ ์ํด ๋ฆฌ๋ทฐ๋ฐ์ง ์์์ต๋๋ค. ํด๋น ๋ฒ์ญ๋ณธ์ ์ ๋ฐ์ดํธํ๊ณ ์ถ๋ค๋ฉด ํด๋น ๋ฒ์ญ๋ณธ์ ๋ํ ์ด์๋ฅผ ์ ๊ธฐํ์ญ์์ค.
Alex Reardon - @alexandereardon - areardon@atlassian.com