Skip to content

LeeHyungGeun/react-beautiful-dnd-kr

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

3 Commits
ย 
ย 

Repository files navigation

์›๋ฌธ:https://github.com/atlassian/react-beautiful-dnd
์ตœ์ข… ๋ฒˆ์—ญ ์ผ์ž: 2017๋…„08์›”23์ผ(์ˆ˜) 22์‹œ00๋ถ„ UTC+9 ์›๋ณธ

react-beautiful-dnd

์•„๋ฆ„๋‹ต๊ณ  ์ ‘๊ทผ ์šฉ์ดํ•œ Rreact(React.js) ๋ฆฌ์ŠคํŠธ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž

Build Status dependencies SemVer

example

Examples(์˜ˆ์ œ) ๐ŸŽ‰

์–ผ๋งˆ๋‚˜ ์•„๋ฆ„๋‹ค์šด์ง€ ์ง์ ‘ ๋ณด์„ธ์š” - have a play with the examples!

Core characteristics(ํ•ต์‹ฌ ํŠน์ง•):

  • ์•„๋ฆ„๋‹ต๊ณ  ์ž์—ฐ์Šค๋Ÿฌ์šด ์•„์ดํ…œ ์ด๋™
  • ๊น”๋”ํ•˜๊ณ  ๊ฐ•๋ ฅํ•œ API ๋ฅผ ํ†ตํ•œ ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ
  • ๋…์ฐฉ์ •์ธ ์Šคํƒ€์ผ
  • ์ถ”๊ฐ€ DOM ์ƒ์„ฑ์ด ํ•„์š” ์—†์Œ - ์นœ์ˆ™ํ•œ flexbox ์™€ focus ๊ด€๋ฆฌ
  • anchor ํƒœ๊ทธ ๋“ฑ ๊ธฐ์กด ๋…ธ๋“œ์™€ ์ž˜ ์ž‘๋™
  • ์ƒํƒœ(state) ์ฃผ๋„์˜ ๋“œ๋ž˜๊น… - ์ด ๋ถ€๋ถ„์€ ํ”„๋กœ๊ทธ๋ž˜๋ฐ(programatic) ๋ฐฉ์‹์œผ๋กœ ๋งŽ์€ input ํƒ€์ž…์—์„œ ๋“œ๋ž˜๊น…(dragging)์„ ํ—ˆ์šฉ ํ•œ๋‹ค. ํ˜„์žฌ๋Š” ๋งˆ์šฐ์Šค ์™€ ํ‚ค๋ณด๋“œ ๋“œ๋ž˜๊น…(dragging)๋งŒ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

Not for everyone(์ฐธ๊ณ ์‚ฌํ•ญ)

React๋ฅผ ์‚ฌ์šฉํ•œ ๋งŽ์€ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ์ค‘ ๊ฐ€์žฅ ์ฃผ๋ชฉํ•  ๊ฒƒ์€ react-dnd ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ HTML5 ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž feature(wildly inconsistent)๊ณผ ํ›Œ๋ฅญํ•˜๊ฒŒ ๋งค์น˜๋˜๋Š” ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž์„ ์ง€์› ํ•ฉ๋‹ˆ๋‹ค. react-beautiful-dnd ๋Š” vertical ๊ณผ horizontal ๋ฆฌ์ŠคํŠธ๋ฅผ ์œ„ํ•ด ํŠน๋ณ„ํžˆ high level๋กœ ์ถ”์ƒํ™” ๋˜์–ด์žˆ์Šต๋‹ˆ๋‹ค. react-beautiful-dnd๋Š” ๊ธฐ๋Šฅ์ ์ธ subset์œผ๋กœ ํŒŒ์›Œํ’€ํ•˜๊ณ  ์ž์—ฐ์Šค๋Ÿฝ๊ณ  ์•„๋ฆ„๋‹ค์šด ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž์„ ์ œ์•ˆํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ๊ทธ๊ฒƒ์€ react-dnd ์ฒ˜๋Ÿผ ๋งŽ์€ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ๋‹น์‹ ์˜ ๊ฒฝ์šฐ์— ๋งž์ง€ ์•Š์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Still young!(์•„์ง ์ Š์Šต๋‹ˆ๋‹ค!)

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์ƒˆ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ƒ๋Œ€์ ์œผ๋กœ ์ž‘์€ ๊ธฐ๋Šฅ๋งŒ ์ œ๊ณต ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋‹ฌ๋ ค ์ฃผ์„ธ์š”! ๋น ๋ฅด๊ฒŒ ์›€์ง์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค!

Currently supported feature set(ํ˜„์žฌ ์ง€์›ํ•˜๋Š” ํŠน์ง•)

  • vertical ๋ฆฌ์ŠคํŠธ
  • horizontal ๋ฆฌ์ŠคํŠธ
  • ํ•œ ํŽ˜์ด์ง€ ์•ˆ์˜ ๋ฉ€ํ‹ฐ ๋…๋ฆฝ ๋ฆฌ์ŠคํŠธ
  • ๋งˆ์šฐ์Šค ๐Ÿญ ์™€ ํ‚ค๋ณด๋“œ ๐ŸŽน ๋“œ๋ž˜๊น…
  • ๋…๋ฆฝ ์ค‘์ฒฉ ๋ฆฌ์ŠคํŠธ(๋ฆฌ์ŠคํŠธ๋Š” ๋‹ค๋ฅธ ๋ฆฌ์ŠคํŠธ์˜ ์ž์‹์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋‹น์‹ ์€ ๋ถ€๋ชจ ๋ฆฌ์ŠคํŠธ์˜ ์•„์ดํ…œ์„ ์ž์‹ ๋ฆฌ์ŠคํŠธ๋กœ ๋“œ๋ž˜๊ทธ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.)
  • ๊ฐ€๋ณ€ ๋†’์ด ์•„์ดํ…œ (์•„์ดํ…œ์€ ์„œ๋กœ ๋‹ค๋ฅธ ๋†’์ด๋ฅผ ๊ฐ€์งˆ์ˆ˜ ์žˆ๋‹ค.)
  • ์ปค์Šคํ…€ ๋“œ๋ž˜๊ทธ ํ•ธ๋“ค (์ผ๋ถ€ ์•„์ดํ…œ๋งŒ ๋“œ๋ž˜๊ทธ ํ•  ์ˆ˜ ์žˆ๋‹ค.)
  • vertical ๋ฆฌ์ŠคํŠธ๋Š” scoll container๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. (์Šคํฌ๋กค ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ ์—†์ด) ํ˜น์€ ์ž์‹์ด scoll container๊ฐ€ ๋  ์ˆ˜ ์žˆ๋‹ค. (์—ญ์‹œ ์Šคํฌ๋กค ๊ฐ€๋Šฅํ•œ ๋ถ€๋ชจ ์—†๋‹ค)

Basic usage example(๊ธฐ๋ณธ ์‚ฌ์šฉ ์˜ˆ์ œ)

์ด๊ฒƒ์€ ๊ฐ„๋‹จํ•œ ์žฌ์ •๋ ฌ ๋ฆฌ์ŠคํŠธ ์ž…๋‹ˆ๋‹ค. You can play with it on webpackbin

basic example

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'));

Physicality(๋ฌผ๋ฆฌ์ )

react-beautiful-dnd์˜ ํ•ต์‹ฌ ๋””์ž์ธ์€ ๋ฌผ๋ฆฌ์ ์ž…๋‹ˆ๋‹ค: ์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๊ทธ๋“ค์ด ๋ฌผ๋ฆฌ์  ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์›€์ง์ธ๋‹ค๊ณ  ๋Š๋ผ๊ธธ ์›ํ•ฉ๋‹ˆ๋‹ค.

Application 1: no instant movement(์ฆ‰๊ฐ์ ์ธ ์›€์ง์ž„์ด ์—†์Šต๋‹ˆ๋‹ค.)

์ด๊ฒƒ์€ ์‚ฌ์šฉ์ž ๋“œ๋ž˜๊ทธ์— ๋ฐ˜์‘ํ•˜๋Š” ๊ฒƒ์€ ํ‘œ์ค€ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž ํŒจํ„ด์ž…๋‹ˆ๋‹ค. ๋ณด๋‹ค์šด์ž์—ฐ์Šค๋Ÿฌ์šด ๋“œ๋ž˜๊ทธ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์œ„ํ•ด ๋ช…ํ™•ํ•œ ๋“œ๋ž˜๊ทธ ํšจ๊ณผ๋ฅผ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์—ญ์‹œ ์ƒˆ๋กœ์šด ์œ„์น˜๋กœ ์•„์ดํ…œ์„ ๋“œ๋žํ• ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์•„๋ฌด ๊ณณ์œผ๋กœ๋‚˜ ์•„์ดํ…œ์„ ์ด๋™ํ•  ์ˆ˜๋Š” ์—†์Šต๋‹ˆ๋‹ค. ๋“œ๋ž˜๊ทธ ํ•˜๊ณ  ๋ง๊ณ ๋Š” ๊ด€๋ จ ์—†์Šต๋‹ˆ๋‹ค.

Application 2: knowing when to move(์–ธ์ œ ์›€์ง์ผ๊นŒ์š”.)

๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž ์ธํ„ฐ๋ ‰์…˜์€ ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธ ์‹œ์ž‘ํ•œ ๊ณณ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๋Š” ๊ฒƒ์ด ์ผ๋ฐ˜์ ์ž…๋‹ˆ๋‹ค.

react-beautiful-dnd์˜ ์•„์ดํ…œ ๋“œ๋ž˜๊น… ํšจ๊ณผ๋Š” ์•„์ดํ…œ์˜ ์ค‘๋ ฅ ํšจ๊ณผ๋ฅผ ๊ธฐ๋ณธ์œผ๋กœ ํ•ฉ๋‹ˆ๋‹ค. - ๋‹น์‹ ์ด ์•„์ดํ…œ์„ ์–ด๋””์—์„œ ์žก์•˜๋Š”์ง€๋Š” ๊ด€๊ณ„ ์—†์Šต๋‹ˆ๋‹ค. ์•„์ดํ…œ ๋“œ๋ž˜๊น… ํšจ๊ณผ๋Š” ์Šค์ผ€์ผ ์„ค์ •โš–๏ธ๊ณผ ์œ ์‚ฌํ•œ ๊ทœ์น™์„ ๋”ฐ๋ฆ…๋‹ˆ๋‹ค. ๊ฐ€๋ณ€ ๋†’์ด ์•„์ดํ…œ์˜ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋“œ๋ž˜๊ทธ๋ฅผ ์œ„ํ•ด ๋ช‡ ๊ฐ€์ง€ ๊ทœ์น™์„ ํ—ˆ์šฉ ํ•ฉ๋‹ˆ๋‹ค.

  • ๋ฆฌ์ŠคํŠธ๋Š” ๋ฆฌ์ŠคํŠธ์˜ ๋ฐ”์šด๋”๋ฆฌ๊นŒ์ง€ ์•„์ดํ…œ ๋“œ๋ž˜๊น… ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.
  • ๋“œ๋ž˜๊ทธ ์•„์ดํ…œ์˜ ์ค‘์‹ฌ ํฌ์ง€์…˜์ด ์•„์ดํ…œ์˜ ๊ฒฝ๊ฒŒ๋กœ ๋“œ๋ž˜๊ทธ ๋˜๋ฉด ๋‚˜๋จธ์ง€ ์•„์ดํ…œ๋“ค์€ ์›€์ง์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅด๊ฒŒ ๋งํ•˜์ž๋ฉด: (A) ์•„์ดํ…œ์˜ ์ค‘์‹ฌ ํฌ์ง€์…˜์ด ๋์œผ๋กœ ์ด๋™๋˜๋ฉด ๋‹ค๋ฅธ ์•„์ดํ…œ (B)๋Š” ์›€์ง์ด๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

Application 3: no drop shadows(๋“œ๋ž ์‰๋„์šฐ๋Š” ์—†์Šต๋‹ˆ๋‹ค.)

๋“œ๋ž ์‰๋„์šฐ๋Š” ์•„์ดํ…œ๊ณผ ๊ทธ๊ฒƒ์˜ ๋ชฉํ‘œ๊ฐ€ ๋ฐ”๋€Œ๋Š” ํ™˜๊ฒฝ์—์„œ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ react-beautiful-dnd๋Š” ์–ด๋””์— ์•„์ดํ…œ์ด ๋“œ๋ž๋ ์ง€๊ฐ€ ๋ช…ํ™•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ถ”ํ›„์— ๋ณ€๊ฒฝ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. - ๊ทธ๋Ÿฌ๋‚˜ ์ด๋Ÿฐ ํšจ๊ณผ ์—†์ด ์–ด๋”” ๊นŒ์ง€ ๋ณผ์ˆ˜ ์žˆ์„์ง€ ์‹คํ—˜ํ•ด ๋ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Application 4: maximise interactivity(์Œ๋ฐฉํ–ฅ ๊ทน๋Œ€ํ™”)

react-beautiful-dnd๋Š” ์ตœ๋Œ€ํ•œ ์ธํ„ฐ๋ ‰ํ‹ฐ๋ธŒ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•œ ๊ธฐ๊ฐ„์„ ํ”ผํ•˜๋ ค๊ณ  ๋…ธ๋ ฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์‚ฌ์šฉ์ž๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ๋๋‚˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆด ํ•„์š” ์—†์ด ์ง€์†์ ์œผ๋กœ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ํ†ตํ•ด ์ธํ„ฐ๋ ‰์…˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜, ๋ชจ๋‘๊ฐ€ ๋ถ„๋ณ„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ •ํ™•์„ฑ๊ณผ ํž˜ ์‚ฌ์ด์—์„œ ๊ท ํ˜•์„ ์œ ์ง€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์— ์ธํ„ฐ๋ ‰ํ‹ฐ๋ธŒ ํ•˜์ง€ ์•Š์€ ๋ช‡ ๊ฐ€์ง€ ์ƒํ™ฉ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  1. ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์™„๋ฃŒํ•  ๋•Œ ๋“œ๋ž˜๊ทธ๋ฅผ ์ทจ์†Œํ•  ๊ฒฝ์šฐ, ๊ทธ๋“ค์„ ์ตœ๋Œ€ํ•œ ๋˜๋Œ๋ฆฝ๋‹ˆ๋‹ค. ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ์œ„์น˜์— ๋“œ๋ž˜๊ทธ๋ฅผ ํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค.
  2. ๋“œ๋ž๋˜๋Š” ์•„์ดํ…œ์ด ๋“œ๋ž˜๊ทธ ๋ฉ๋‹ˆ๋‹ค. ๊ฐ„๋‹จํ•˜๊ฒŒ ์ด ๊ฒฝ์šฐ ์ž…๋‹ˆ๋‹ค - ์›๋ž˜ ์œ„์น˜๋กœ ์›€์ง์ด๊ณ  ์žˆ๋Š” ์•„์ดํ…œ์„ ์žก๊ธฐ๋Š” ์—„์ฒญ ํž˜๋“ญ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ฝ”๋”ฉ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค - ๊ทธ๋Ÿฌ๋‚˜ ๋งŽ์€ ๋ณต์žกํ•œ ์ผ€์ด์Šค๊ฐ€ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

ํ•ญ์ƒ ๋น„ํ™œ์„ฑ ๊ธฐ๊ฐ„์ด ์กด์žฌํ•˜๋Š” ๊ฒƒ์€ ์•„๋‹™๋‹ˆ๋‹ค.

Application 5: no drag axis locking(๋“œ๋ž˜๊ทธ ์ถ• ์ž ๊ธˆ ์—†์Œ)

์ง€๊ธˆ ๊นŒ์ง€ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋“œ๋ž˜๊ทธ ์ถ• ์ž๊ธˆ์„ ์ง€์›ํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. (๋“œ๋ž˜๊ทธ ๋ ˆ์ผ์Šค(rails)๋ผ๊ณ ๋„ ๋ถˆ๋ฆฝ๋‹ˆ๋‹ค.) ์ด๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ํ•œ ์ถ•์œผ๋กœ๋งŒ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•˜๋„๋ก ์ œ์•ˆํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์ƒ๊ฐ์œผ๋กœ๋Š” ์ด๊ฒƒ์€ ๋ฌผ๋ฆฌ์  ๋น„์œ ๋ฅผ ํ—ค์นฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ฌผ๋ฆฐ์ ์ธ ๋ฌผ์ฒด๋ฅผ ์›€์ง์ด๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ๋ฉ”์„ธ์ง€๋ฅผ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ณด๋‚ด๋ฉด์„œ ์ƒํ˜ธ์ž‘์šฉ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ type ๊ณผ isDropEnable props๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ•˜๋‚˜์˜ ๋ฆฌ์ŠคํŠธ์—๋งŒ ๋“œ๋žํ•  ์ˆ˜ ์žˆ๋„๋ก ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค. ๋‹น์‹ ์€ onDragStart ๋ฆฌ์ŠคํŠธ ์‹œ๊ฐ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•˜์—ฌ ์ธํ„ฐ๋ ‰ํŠธํ•  ์ˆ˜ ์žˆ๋Š” ์œ ์ผํ•œ ์žฅ์†Œ์ž„์„ ๋ณด์—ฌ์ค„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Sloppy clicks and click blocking(์—‰์„ฑํ•œ ํด๋ฆญ ๋ฐ ํด๋ฆญ ์ฐจ๋‹จ) ๐Ÿฑ๐ŸŽ

์‚ฌ์šฉ์ž๊ฐ€ ์—˜๋ฆฌ๋ฉ˜ํŠธ์— ๋งˆ์šฐ์Šค ๋‹ค์šด์„ ๋ˆ„๋ฅผ ๊ฒฝ์šฐ, ์šฐ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญ์„ ํ•œ ๊ฑด์ง€ ๋“œ๋ž˜๊ทธ๋ฅผ ํ•˜๋Š” ๊ฑด์ง€ ์•Œ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋•Œ๋•Œ๋กœ ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•  ๋•Œ ์ปค์„œ๋ฅผ ์•ฝ๊ฐ„ ์›€์ง์ด๋Š” ๊ฒฝ์šฐ๋„ ์žˆ์Šต๋‹ˆ๋‹ค. - ์—‰์„ฑํ•œ ํด๋ฆญ. ๊ทธ๋ž˜์„œ ์šฐ๋ฆฌ๋Š” ์ผ์ •ํ•œ ๊ฑฐ๋ฆฌ๋ฅผ ๋งˆ์šฐ์Šค ๋‹ค์šด๊ณผ ํ•จ๊ป˜ ์›€์ง์ผ ๊ฒฝ์šฐ ๋“œ๋ž˜๊ทธ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค. - ์—‰์„ฑํ•œ ํด๋ฆญ์„ ๋งŒ๋“œ๋Š” ๊ฒƒ๋ณด๋‹ค ๋‚ซ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋“œ๋ž˜๊ทธ ํ•œ๊ณ„์‹œ์ ์„ ๋„˜๊ธฐ์ง€ ์•Š๊ฒŒ ๋˜๋ฉด ์‚ฌ์šฉ์ž ์ธํ„ฐ๋ ‰์…˜์€ ์ผ๋ฐ˜์ ์ธ ํด๋ฆญ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋“œ๋ž˜๊ทธ ํ•œ๊ณ„์‹œ์ ์ด ์ง€๋‚˜๋ฉด ์ธํ„ฐ๋ ‰์…˜์€ ๋“œ๋ž˜๊ทธ ๋˜๊ณ  ๊ธฐ๋ณธ ํด๋ฆญ ์•ก์…˜์€ ์ผ์–ด๋‚˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์€ ์‚ฌ์šฉ์ž๊ฐ€ ์ธํ„ฐ๋ ‰ํ‹ฐ๋ธŒ ์—˜๋ฆฌ๋ฒˆํŠธ๋ฅผ ๋ž˜ํ•‘ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•˜๋ฉฐ ์ž์—ฐ์Šค๋Ÿฌ์šด ๋ฐฉ์‹์œผ๋กœ ๊ธฐ๋ณธ ์•ต์ปค๋ฟ์™€ ๋“œ๋ž˜๊ทธ ๊ฐ€๋Šฅํ•œ ์•„์ดํ…œ์„ ๊ฐ€์งˆ ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

(๐Ÿฑ๐ŸŽ is a schrodinger's cat joke)

Focus management(ํฌ์ปค์Šค ๊ด€๋ฆฌ)

react-beautiful-dnd์€ ์–ด๋–ค ๋ž˜ํผ ์—˜๋ฆฌ๋จผํŠธ๋„ ์ƒ์„ฑํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฆ‰ ๋ฌธ์„œ์˜ ์ผ๋ฐ˜์ ์ธ ํƒญ ํ๋ฆ„์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด anchor(์•ต์ปค) ํƒœ๊ทธ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋Š” *anchor(์•ต์ปค)*๋ฅผ ๋‘˜๋Ÿฌ์‹ผ ์š”์†Œ๊ฐ€ ์•„๋‹Œ ์•ต์ปค์— ์ง์ ‘ ํƒญ์„ ๊ฒ๋‹ˆ๋‹ค. ๋‹น์‹ ์ด ์–ด๋–ค ์—˜๋ฆฌ๋จผํŠธ์— ๋ž˜ํ•‘์„ ํ•˜๋˜์ง€ ํ‚ค๋ณด๋“œ ๋“œ๋ž˜๊น…์„ ์œ„ํ•ด tab-index ๊ฐ€ ์ฃผ์–ด์ง‘๋‹ˆ๋‹ค.

Accessibility(์ ‘๊ทผ์„ฑ)

์ „ํ†ต์ ์œผ๋กœ ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž ์ธํ„ฐ๋ ‰์…˜์€ ์˜ค์ง ๋งˆ์šฐ์Šค์™€ ํ„ฐ์น˜ ์ธํ„ฐ๋ ‰์…˜๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” using only a keyboard(์˜ค์ง ํ‚ค๋ณด๋“œ๋ฅผ ํ†ตํ•œ) ๋“œ๋ž˜๊ทธ ์ธํ„ฐ๋ ‰์…˜๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ๊ณ ๊ธ‰ ์‚ฌ์šฉ์ž๋Š” ํ‚ค๋ณด๋“œ๋ฅผ ํ†ตํ•ด ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž์„ ๊ฒฝํ—˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ์ด์ „์— ์ œ์™ธ ๋˜์—ˆ๋˜ ์‚ฌ์šฉ์ž๋“ค์—๊ฒŒ๋„ ์ œ๊ณตํ• ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

ํ‚ค๋ณด๋“œ ์ง€์› ์™ธ์—๋„ ์šฐ๋ฆฌ๋Š” ํ‘œ์ค€ ๋ธŒ๋ผ์šฐ์ € ํ‚ค๋ณด๋“œ ์ธํ„ฐ๋ ‰์…˜ ๋ฐฉ์‹์œผ๋กœ ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค๋ฅผ ๊ฒ€์‚ฌ ํ–ˆ์Šต๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊ทธ ํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์‚ฌ์šฉ์ž๋Š” ๋ณดํ†ต ํ‚ค๋ณด๋“œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋“œ๋ž˜๊ทธ ํ•˜๋Š” ๋™์•ˆ ์šฐ๋ฆฌ๋Š” ๋ธŒ๋ผ์šฐ์ € ๋‹จ์ถ•ํ‚ค(tab๊ณผ ๊ฐ™์€)๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ๋น„ํ™œ์„ฑํ•˜์—ฌ ์‚ฌ์šฉ์ž์—๊ฒŒ ๋ถ€๋“œ๋Ÿฌ์šด ๊ฒฝํ—˜์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

Shortcuts(๋‹จ์ถ•ํ‚ค)

ํ˜„์žฌ๋Š” ํ‚ค๋ณด๋“œ ์ฒ˜๋ฆฌ๊ฐ€ ํ•˜๋“œ ์ฝ”๋”ฉ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ถ”ํ›„์— ์ปค์Šคํ„ฐ๋งˆ์ด์ง•ํ•  ์ˆ˜ ์žˆ๋„๋ก ์ˆ˜์ •๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์ด๊ฒƒ์ด ํ˜„์žฌ์˜ ํ‚ค๋ณด๋“œ ๋งคํ•‘์ž…๋‹ˆ๋‹ค.:

  • tab tab โ†น - ํ‘œ์ค€ ๋ธŒ๋ผ์šฐ์ € ํƒญ์€ Droppable ๋„ค๋น„๊ฒŒ์ดํŠธ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ tab์„ ์„ ํƒํ•˜๋Š” ๋™์•ˆ ์–ด๋–ค ํ™”๋ คํ•œ ๋™์ž‘๋„ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•œ๋ฒˆ ๋“œ๋ž˜๊ทธ๋ฅผ ์‹œ์ž‘ํ•˜๋ฉด ๋“œ๋ž˜๊ทธ ์ง€์†์„ ์œ„ํ•ด tab์€ ๋ธ”๋ฝ ๋ฉ๋‹ˆ๋‹ค.
  • spacebar space - ํฌ์ปค์Šค๋œ Draggable์„ ๋“ค์–ด ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค. ๋˜ํ•œ spacebar๋กœ ๋“œ๋ž˜๊ทธ๊ฐ€ ์‹œ์ž‘๋œ ๊ณณ์—์„œ ๋“œ๋ž˜๊ทธ ํ•˜๋Š” Draggable์„ ๋“œ๋žํ•ฉ๋‹ˆ๋‹ค.
  • Up arrow โ†‘ - vertical ๋ฆฌ์ŠคํŠธ์—์„œ Draggable์„ ์œ„๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  • Down arroa โ†“ - vertical ๋ฆฌ์ŠคํŠธ์—์„œ Draggable์„ ์•„๋ž˜๋กœ ์ด๋™ํ•ฉ๋‹ˆ๋‹ค.
  • Escape esc - ๋“œ๋ž˜๊ทธ ์ค‘์ธ ๊ฒƒ์„ ์ทจ์†Œ ํ•ฉ๋‹ˆ๋‹ค. - ์‚ฌ์šฉ์ž๊ฐ€ ํ‚ค๋ณด๋“œ ๋˜๋Š” ๋งˆ์šฐ์Šค๋กœ ๋“œ๋ž˜๊ทธ ํ•˜๊ณ  ์žˆ๋Š”๊ฒƒ๊ณผ ๊ด€๊ณ„ ์—†์ด.

Limitations of keyboard dragging(ํ‚ค๋ณด๋“œ ๋“œ๋ž˜๊ทธ์‹œ ์ œ์•ˆ ์‚ฌํ•ญ)

ํ˜„์žฌ ํ‚ค๋ณด๋“œ ๋“œ๋ž˜๊ทธ์˜ ์ œ์•ˆ ์‚ฌํ•ญ ์ž…๋‹ˆ๋‹ค: ์‚ฌ์šฉ์ž๊ฐ€ ์œˆ๋„์šฐ๋ฅผ ์Šคํฌ๋กคํ•˜๋ฉด ๋“œ๋ž˜๊ทธ๊ฐ€ ์ทจ์†Œ ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ผ์–ด๋‚  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์œผ๋กœ์„œ๋Š” ๊ฐ€์žฅ ๊ธฐ๋ณธ์ ์ธ ์ ‘๊ทผ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

Carefully designed animations(์กฐ์‹ฌ์Šค๋Ÿฝ๊ฒŒ ๋””์ž์ธ๋œ ์• ๋‹ˆ๋ฉ”์ด์…˜)

์• ๋‹ˆ๋ฉ”์ด์…˜์„ ํ†ตํ•ด ๋งŽ์€ ๊ฒƒ์„ ์›€์ง์ผ๋•Œ ์‚ฌ์šฉ์ž๋Š” ์‚ฐ๋งŒํ•ด ์ง€๋ฉฐ ๋ฐฉํ•ด๊ฐ€ ๋ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ๋ฐธ๋Ÿฐ์Šค์™€ ์ธํ„ฐ๋ž™์…˜ ํผํฌ๋จผ์Šค๋ฅผ ๋ณด์žฅํ•˜๊ธฐ ์œ„ํ•ด ๋งŽ์€ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ˆ˜์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

Dropping(๋“œ๋ž)

๋‹น์‹ ์ด ์›€์ง์ด๋Š” ์•„์ดํ…œ์„ ๋“œ๋žํ•  ๋•Œ์˜ ์›€์ง์ž„์€ ๋ฌผ๋ฆฌํ•™์„ ๊ธฐ์ดˆ๋กœ ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค (thanks react-motion). ๊ฒฐ๊ณผ์ ์œผ๋กœ ๋“œ๋ž ๋Š๋‚Œ์ด ๋” ๊ฐ€์ค‘๋˜๊ณ  ๋ฌผ๋ฆฌ์ ์œผ๋กœ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.

Moving out of the way(์›€์ง์ž„)

๋“œ๋ž˜๊ทธ ํ•˜๋Š” ์•„์ดํ…œ์˜ ์›€์ง์ž„์€ CSS transition์œผ๋กœ ํ‘œํ˜„ํ•˜๋Š” ๊ฒƒ์ด ๋ฌผ๋ฆฌ์ ์ธ ๊ฒƒ๋ณด๋‹ค ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ GPU์œผ๋กœ ์›€์ง์ž„์„ ํ•ธ๋“คํ•˜๋ฉฐ ํผํฌ๋จผ์Šค๋ฅผ ๊ทน๋Œ€ํ™”ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณก์„ ์€ ๋ฐฉํ•ด๋ฐ›์ง€ ์•Š๊ณ  ๋“œ๋ž˜๊ทธ ํ•˜๋Š” ์•„์ดํ…œ์ด ๋ฐ–์œผ๋กœ ์›€์ง์ด๋ฉด CSS transition ์ด ๋ฌผ๋ฆฌ์  ์ธ ์ข‹์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ GPU๋กœ ์›€์ง์ž„์„ ํ•ธ๋“คํ•˜๊ฒŒ ํ•˜๋ฉด์„œ ํผํฌ๋จผ์Šค๋ฅผ ๊ทน๋Œ€ํ™” ํ•ฉ๋‹ˆ๋‹ค. CSS ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณก์„ ์€ ์ปค๋ฎค๋‹ˆ์ผ€์ด์…˜ ํ•  ์ˆ˜ ์žˆ๋„๋ก ์„ค๊ณ„ ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑ๋˜์—ˆ์Šต๋‹ˆ๋‹ค:

  1. ์ž์—ฐ์Šค๋Ÿฌ์šด ์‘๋‹ต ์†๋„๋กœ ๋ณด์ด๋Š” ์ค€๋น„ ์‹œ๊ฐ„
  2. ๋น ๋ฅด๊ฒŒ ์›€์ง์ผ์ˆ˜ ์žˆ๋Š” ์ž‘์€ ๋‹จ๊ณ„
  3. ๊ธด ํ›„๋ฐ˜๋ถ€ ๊ทธ๋ž˜์„œ ์‚ฌ๋žŒ๋“ค์€ ํ›„๋ฐ˜๋ถ€์—๋‚˜ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๋œ ํ…์ŠคํŠธ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ๋‹ค

animation curve(์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณก์„ ))

์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ณก์„ ๋Š” ์›€์ง์ผ๋•Œ ์‚ฌ์šฉ๋œ๋‹ค.

Installation(์„ค์น˜)

# yarn
yarn add react-beautiful-dnd

# npm
npm install react-beautiful-dnd --save

API

๊ทธ๋ž˜์„œ ์–ด๋–ป๊ฒŒ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ• ๊นŒ์š”?

DragDropContext

๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋‹น์‹ ์€ ๋žฉํ•‘๋˜์–ด ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋ž์„ ํ•  ์ˆ˜ ์žˆ๋Š” DragDropContext React ํŠธ๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ DragDropContext์œผ๋กœ ์ „์ฒด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋žฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์Šต๋‹ˆ๋‹ค. ์ค‘์ฒฉ(nested) DragDropContext๋Š” ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์€ Droppable ๊ณผ Draggable props ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์กฐ๊ฑด๋ถ€ ๋“œ๋ž˜๊ทธ ๋“œ๋ž์„ ํ• ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DragDropContext๋Š” react-redux Provider component์™€ ๋น„์Šทํ•œ ์šฉ๋„๋ผ๊ณ  ๋ณด์‹œ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Prop type information(Prop ํƒ€์ž… ์ •๋ณด)

type Hooks = {|
  onDragStart?: (id: DraggableId, location: DraggableLocation) => void,
  onDragEnd: (result: DropResult) => void,
|}

type Props = Hooks & {|
  children?: ReactElement,
|}

Basic usage(๊ธฐ๋ณธ ์‚ฌ์šฉ๋ฒ•)

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>
    )
  }
}

Hooks

๋‹น์‹ ์˜ ์Šคํ…Œ์ดํŠธ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋Š” ๋งŽ์€ ํƒ‘ ๋ ˆ๋ฒจ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ด๋ฒคํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

onDragStart (optional)

์ด ํ•จ์ˆ˜๋Š” ๋“œ๋ž˜๊ทธ๋ฅผ ์‹œ์ž‘ํ•  ๋•Œ ์•Œ๋ฆผ๋ฐ›์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹น์‹ ์€ ์•„๋ž˜ ์‚ฌํ•ญ๋“ค์„ ์ œ๊ณต๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

  • 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;

onDragEnd (required ํ•„์ˆ˜)

์ด ํ•จ์ˆ˜๋Š” ์—„์ฒญ ์ค‘์š”ํ•˜๋ฉฐ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์—์„œ lifecycle ์œ„ํ—˜ํ•œ ์—ญํ• ์„ ๋‹ด๋‹นํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ๋ฐ˜๋“œ์‹œ Draggables ๋ฆฌ์ŠคํŠธ์˜ ๋™๊ธฐ ๋ฐฉ์‹์œผ๋กœ ์žฌ์ •๋ ฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๊ฒƒ์€ ๋“œ๋ž˜๊ทธ์— ๋Œ€ํ•œ ๋ชจ๋“  ์ •๋ณด๊ฐ€ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค:

result: DropResult

  • result.draggableId: ๋“œ๋ž˜๊ทธ ๋˜์—ˆ๋˜ Draggable์˜ id.
  • result.type: ๋“œ๋ž˜๊ทธ ๋˜์—ˆ๋˜ Draggable์˜ type.
  • result.source: Draggable ์ด ์‹œ์ž‘๋œ ์œ„์น˜(location).
  • result.destination: Draggable์ด ๋๋‚œ ์œ„์น˜(location). ๋งŒ์•ฝ์— Draggable์ด ์‹œ์ž‘ํ•œ ์œ„์น˜์™€ ๊ฐ™์€ ์œ„์น˜๋กœ ๋Œ์•„์˜ค๋ฉด ์ด destination๊ฐ’์€ null์ด ๋ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Synchronous reordering(๋™๊ธฐ๋ฐฉ์‹ ์žฌ์ •๋ ฌ)

์™œ๋ƒํ•˜๋ฉด ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋‹น์‹ ์˜ state๋ฅผ ์ œ์–ดํ•  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์— ๊ฒฐ๊ณผ์— ๋”ฐ๋ผ ๋™๊ธฐ์‹ ๊ฒฐ๊ณผ๋ฅผ ์žฌ์ •๋ ฌ ํ•˜๋Š” ๊ฒƒ์€ ๋‹น์‹ ์— ๋‹ฌ๋ ค์žˆ์Šต๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์— ๋‹น์‹ ์ด ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋Š”๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๋‹ค.!

  • ๋งŒ์•ฝ destination์ด null์ด๋ฉด: ๋ชจ๋‘ ์™„๋ฃŒ!
  • ๋งŒ์•ฝ source.droppableId ๊ฐ€ destination.droppableId์™€ ๊ฐ™์œผ๋ฉด ๋‹น์‹ ์€ ๋‹น์‹ ์˜ ๋ฆฌ์ŠคํŠธ์—์„œ ์•„์ดํ…œ์„ ์ œ๊ฑฐํ•˜๊ณ  ์˜ฌ๋ฐ”๋ฅธ ์œ„์น˜์— ์‚ฝ์ž…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • ๋งŒ์•ฝ source.droppableId๊ฐ€ destination.droppable๊ณผ ๋‹ค๋ฅด๋ฉด ๋‹น์‹ ์€ source.droppableId ๋ฆฌ์ŠคํŠธ์—์„œ Draggable ํ•˜๊ณ  ๊ทธ๊ฒƒ์„ destination.droppableId ๋ฆฌ์ŠคํŠธ์˜ ์˜ฌ๋ฐ”๋ฅธ ์œ„์น˜์— ์ถ”๊ฐ€ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Type information(ํƒ€์ž… ์ •๋ณด)

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
|};

Best practices for hooks (hooks์„ ์œ„ํ•œ ์ตœ๊ณ ์˜ ๋ฐฉ๋ฒ•)

Block updates during a drag(๋“œ๋ž˜๊ทธ ํ•˜๋Š” ๋™์•ˆ ์—…๋ฐ์ดํŠธ๋ฅผ ๋ธ”๋ฝ ํ•˜์„ธ์š”)

์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊น… ํ•˜๋Š” ๋™์•ˆ Draggable๊ณผ Droppable ํ˜น์€ ์น˜์ˆ˜์˜ ๊ฒฐ๊ณผ์— ์˜ํ–ฅ์„ ์ค„ ์ˆ˜ ์žˆ๋Š” ๋ชจ๋“  ์—…๋ฐ์ดํŠธ๋ฅผ ์ฐจ๋‹จํ•˜๋Š” ๊ฒƒ์„ ๊ฐ•๋ ฅํžˆ ์ถ”์ฒœํ•ฉ๋‹ˆ๋‹ค. onDragStart ์—์„œ onDragEnd ๊นŒ์ง€ Draggable ๊ณผ Droppable ์˜ ์—…๋ฐ์ดํŠธ๋ฅผ ์ฐจ๋‹จํ•ด ์ฃผ์„ธ์š”.

์‚ฌ์šฉ์ž๊ฐ€ ๋“œ๋ž˜๊น…์„ ์‹œ์ž‘ํ•  ๋•Œ ์šฐ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ Draggable ๊ณผ Droppable ๋…ธ๋“œ ์ฐจ์ˆ˜์˜ ๋ชจ๋“  ์Šค๋ƒ…์ƒท์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ๋“œ๋ž˜๊ทธ ํ•˜๋Š” ๋™์•ˆ ๊ทธ๊ฒƒ๋“ค์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์šฐ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ๋ชจ๋ฅผ ๊ฒƒ ์ž…๋‹ˆ๋‹ค.

์—ฌ๊ธฐ์— ๋‹น์‹ ์ด ๋“œ๋ž˜๊ทธ ํ•˜๋Š” ๋™์•ˆ ๋ณ€๊ฒฝ์„ ํ•ด์„œ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ๋ช‡ ๊ฐ€์ง€ ๋ถ€์กฑํ•œ ์‚ฌ์šฉ์ž ๊ฒฝํ—˜์ด ์žˆ์Šต๋‹ˆ๋‹ค:

  • ๋‹น์‹ ์ด ๋…ธ๋“œ์˜ ์ˆ˜๋ฅผ ๋Š˜๋ฆฌ๋ฉด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์•Œ์ง€ ๋ชปํ•˜๊ฒŒ ๋˜๋ฉฐ ์‚ฌ์šฉ์ž๊ฐ€ ์˜ˆ์ƒํ•˜๋Š”๋ฐ๋กœ ๋™์ž‘ํ•˜์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ๋‹น์‹ ์ด ๋…ธ๋“œ์˜ ์ˆ˜๋ฅผ ์ค„์ด๋ฉด ๋‹น์‹ ์˜ ๋ฆฌ์ŠคํŠธ์—์„œ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ•œ ์›€์ง์ž„๊ณผ ์ฐจ์ด๊ฐ€ ์ƒ๊ธฐ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ๋‹น์‹ ์ด ๋…ธ๋“œ์˜ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด ๊ทธ๊ฒƒ์€ ๋ณ€๊ฒฝ๋œ ๋…ธ๋“œ์™€ ๋‹ค๋ฅธ ๋…ธ๋“œ ๋ชจ๋‘ ์ž˜๋ชป๋œ ์‹œ๊ฐ„์— ์ด๋™ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • ๋‹น์‹ ์ด ๋“œ๋ž˜๊ทธ ๋…ธ๋“œ์˜ ํฌ๊ธฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฒŒ ๋˜๋ฉด ์ •ํ™•ํ•œ ์‹œ๊ฐ„์— ๋‹ค๋ฅธ ๋…ธ๋“œ๋“ค์ด ์›€์ง์ด์ง€ ์•Š๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

onDragStart and onDragEnd pairing(์Œ)

์šฐ๋ฆฌ๋Š” onDragStart ์ด๋ฒคํŠธ๊ฐ€ ๋‹จ์ผ onDragEnd ์ด๋ฒคํŠธ์™€ ์Œ์„ ์ด๋ฃจ๋Š” ๊ฒƒ์„ ๋ณด์žฅํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด๊ฒƒ์ด ํ‹€๋ฆฐ ๊ฒฝ์šฐ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. - ๊ทธ๊ฒƒ์€ ๋ฒ„๊ทธ ์ž…๋‹ˆ๋‹ค. ํ˜„์žฌ ์™ธ๋ถ€์—์„œ ๋“œ๋ž˜๊ทธ๋ฅผ ์ทจ์†Œํ•  ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ์—†์Šต๋‹ˆ๋‹ค..

Style(์Šคํƒ€์ผ)

๋“œ๋ž˜๊ทธ ์ค‘์— ๋‘๊ฐ€์ง€ ์Šคํƒ€์ผ์„ ๋ฐ”๋””(body)์— ์ถ”๊ฐ€ํ•˜๋Š”์„๊ฒƒ์„ ์ถ”์ฒœ ํ•ฉ๋‹ˆ๋‹ค.

  1. user-select: none; ๊ทธ๋ฆฌ๊ณ 
  2. cursor: grab; (ํ˜น์€ ๋“œ๋ž˜๊ทธ ์ค‘์— ๋‹น์‹ ์ด ์›ํ•˜๋Š” ์ปค์„œ(cursor))

user-select: none; ํ…์ŠคํŠธ๋ฅผ ๋“œ๋ž˜๊ทธํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค.

cursor: [your desired cursor]; ์€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด pointer-events: none; ์ด ๋“œ๋ž˜์ด ์•„์ดํ…œ์— ์ ์šฉ๋˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ด๋ ‡๊ฒŒ ๋˜๋ฉด snapshot.isDragging์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋“œ๋ž˜๊ทธ์— ๋‹น์‹ ์˜ ์ปค์„œ๋ฅผ ์„ค์ •ํ•˜์ง€ ๋ชปํ•˜๊ฒŒ ๋œ๋‹ค. (see Draggable).

Dynamic hooks

๋‹น์‹ ์˜ hook ํ•จ์ˆ˜๋Š” ์˜ค์ง ์‹œ์ž‘ํ•  ๋•Œ๋งŒ ์บก์ฒ˜ ๋œ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ๊ทธ ํ›„์— ์ด ํ•จ์ˆ˜๋ฅผ ๋ณ€๊ฒฝํ•˜๋ฉด ์•ˆ๋ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•ฉ๋‹นํ•œ ๊ฒฝ์šฐ๊ฐ€ ์žˆ๊ฒŒ๋˜๋ฉด ํ›…์„ ์ง€์›ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ง€๊ธˆ์€ ์•„๋‹™๋‹ˆ๋‹ค.

Droppable

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>

Props

  • droppableId: ํ•„์ˆ˜ DroppableId(string), ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ๋Œ€ํ•œ ๋“œ๋ž ๊ฐ€๋Šฅ ์—ฌ๋ถ€๋ฅผ ์‹๋ณ„ํ•˜๋Š” ์œ ๋‹ˆํฌ ์‹๋ณ„์ž ์ž…๋‹ˆ๋‹ค. ์ด prop๋Š” ์ˆ˜์ •ํ•˜์ง€ ๋งˆ์„ธ์š” - ํŠนํžˆ ๋“œ๋ž˜๊ทธ ์ค‘
  • type: ์˜ต์…˜ TypeId(string), Draggable ํด๋ž˜์Šค๋ฅผ ๋ฐ›๊ธฐ ์œ„๊ธฐ ์‚ฌ์šฉ ๋ฉ๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, PERSON์„ ์‚ฌ์šฉํ•˜๋ฉด PERSONํƒ€์ž…์˜ Draggable๋งŒ ๋“œ๋ž๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. TASKํƒ€์ž… Draggable์€ PERSON Droppable์— ๋“œ๋ž๋  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ type์ด ์ œ๊ณต๋˜์ง€ ์•Š์œผ๋ฉด ๊ทธ๊ฒƒ์€ DEFAULT๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ Droppable ์ค‘์— Draggable์˜ ์˜type`์€ ๋ฐ˜๋“œ์‹œ ๊ฐ™์•„์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์ด ์ œํ•œ์€ ๋Š์Šจํ•ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • isDropDisabled: ์˜ต์…˜, Droppable์— ๋“œ๋ž์ด ํ—ˆ์šฉ๋˜๋Š”์ง€ ์ œ์–ดํ•˜๋Š” ํ”Œ๋ž˜๊ทธ ์ž…๋‹ˆ๋‹ค. ๋‹น์‹ ์˜ ์กฐ๊ฑด๋ถ€ ๋“œ๋ž ๋กœ์ง์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ false์ž…๋‹ˆ๋‹ค.

Children function(์ž์‹ ํ•จ์ˆ˜)

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>

Conditionally dropping(์กฐ๊ฑด๋ถ€ ๋“œ๋ž)

์œ ์˜ ํ•˜์„ธ์š”. ์ด๋ฒˆ์— ์ด๊ฒƒ์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ์ดˆ๊ธฐ ๋ฒ„์ „์—์„œ๋Š” ์˜ค์ง ๋‹จ์ผ ๋ฆฌ์ŠคํŠธ์˜ ์žฌ์ •๋ ฌ๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค.

  • 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 containers(scroll container)

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” scroll container ๋‚ด์—์„œ์˜ ๋“œ๋ž˜๊ทธ๋ฅผ ์ง€์› ํ•ฉ๋‹ˆ๋‹ค. (DOM ์—˜๋ฆฌ๋จผํŠธ๋Š” overflow: auto; ํ˜น์€ overflow: scroll;๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค.) ์œ ์ผ ํ•˜๊ฒŒ ์ง€์› ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค:

  1. Droppable ์ž์ฒด๊ฐ€ ๋ถ€๋ชจ ์Šคํฌ๋กค์ด ์—†๋Š” scroll container์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  2. 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

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`๋ฅผ ๋ฐ˜ํ™˜ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์กฐ๊ธˆ ์ •๋ฆฌ๋  ์ˆ˜ ์žˆ์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Props

  • 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์ž…๋‹ˆ๋‹ค.

Children function(์ž์‹ ํ•จ์ˆ˜)

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) The Draggable element has position: fixed applied to it while it is dragging. The role of the placeholder is to sit in the place that the Draggable was during a drag. It is needed to stop the Droppable list from collapsing when you drag. It is advised to render it as a sibling to the Draggable node. When the library moves to React 16 the placeholder 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>

Engineering health

Typed

์ด ์ฝ”๋“œ ๋ฒ ์ด์Šค๋Š” flowtype๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‚ด๋ถ€ ์ผ๊ด€์„ฑ์„ ๋†’์ด๊ณ  ํƒ„๋ ฅ์ ์ธ ์ฝ”๋“œ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

Tested

์ด ์ฝ”๋“œ ๋ฒ ์ด์Šค๋Š” ์œ ๋‹›(unit), ํผํฌ๋จผ์Šค(performance) ๊ทธ๋ฆฌ๊ณ  ํ†ตํ•ฉ(integration) ํ…Œ์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜์—ฌ ๋ช‡๊ฐ€์ง€ ๋‹ค๋ฅธ ํ…Œ์ŠคํŠธ ์ „๋žต์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ๋Š” ์‹œ์Šคํ…œ์˜ ๋‹ค์–‘ํ•œ ์ ์—์„œ ํ€„๋ฆฌํ‹ฐ์™€ ์•ˆ์ •์„ฑ์„ ๋†’์ด๋Š” ๊ฒƒ์„ ๋„์™€์ค๋‹ˆ๋‹ค.

์ฝ”๋“œ ์ปค๋ฒ„๋ฆฌ์ง€๋Š” not a guarantee of code health ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์€ ์ข‹์€ ์ง€ํ‘œ์ž…๋‹ˆ๋‹ค. ์ด ์ฝ”๋“œ ๋ฒ ์ด์Šค๋Š” ๊ธฐ์กด ์‚ฌ์ดํŠธ์˜ ~95%๋ฅผ ์ปค๋ฒ„๋ฆฌ์ง€ ํ•ฉ๋‹ˆ๋‹ค.

Performance(ํผํฌ๋จผ์Šค)

์ด ์ฝ”๋“œ๋ฒ ์ด์Šค๋Š” ๋งค์œ„ ๋›ฐ์–ด๋‚œ ํผํฌ๋จผ์Šค๋ฅผ ๋ฐœํœ˜ํ•˜๋„๋ก ์„ค๊ณ„๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค - ๊ทธ๊ฒƒ์˜ 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 and memoize-one
  • all movements are throttled with a requestAnimationFrame - thanks raf-schd
  • memoization is used all over the place - thanks memoize-one
  • conditionally disabling pointer-events on Draggables 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
minimal-browser-paints minimal-react-updates

Supported browsers(์ง€์› ๋ธŒ๋ผ์šฐ์ €)

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” ๋ฐ์Šคํฌํƒ‘์šฉ ํ‘œ์ค€ 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

ํ˜„์žฌ ๋ชจ๋ฐ”์ผ์€ ์ง€์›๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ํ„ฐ์น˜ ์ง€์›ํ•  ๊ณ„ํš์„ ๊ฐ€์ง€๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

๋ฒˆ์—ญ

์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋ฌธ์„œ๋Š” ๋‹ค๋ฅธ ์–ธ์–ด๋กœ๋„ ์ œ๊ณต๋ฉ๋‹ˆ๋‹ค.

์ด ๋ฒˆ์—ญ๋ณธ๋“ค์€ ์ปค๋ฎค๋‹ˆํ‹ฐ์— ์˜ํ•ด ์šด์˜๋˜๊ณ  ์žˆ์œผ๋ฉฐ ๋ฉ”์ธํ„ฐ๋„ค์ด์–ด์— ์˜ํ•ด ๋ฆฌ๋ทฐ๋ฐ›์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ•ด๋‹น ๋ฒˆ์—ญ๋ณธ์„ ์—…๋ฐ์ดํŠธํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด ํ•ด๋‹น ๋ฒˆ์—ญ๋ณธ์— ๋Œ€ํ•œ ์ด์Šˆ๋ฅผ ์ œ๊ธฐํ•˜์‹ญ์‹œ์˜ค.

Author / maintainer

Alex Reardon - @alexandereardon - areardon@atlassian.com

About

react-beautiful-dnd's Korean document.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published