React-dnd: ๋“œ๋ž˜๊ทธํ•˜๋Š” ๋™์•ˆ ์ปค์„œ ์‚ฌ์šฉ์ž ์ง€์ •

์— ๋งŒ๋“  2015๋…„ 11์›” 05์ผ  ยท  15์ฝ”๋ฉ˜ํŠธ  ยท  ์ถœ์ฒ˜: react-dnd/react-dnd

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

์ฆ‰, ๋†“๊ธฐ ์˜์—ญ ์œ„์— ์žˆ๋Š”์ง€ ์—ฌ๋ถ€์— ๊ด€๊ณ„์—†์ด ์ปค์„œ๋Š” ๋™์ผํ•˜๊ฒŒ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์˜ˆ๋ฅผ ๋“ค์–ด Trello๊ฐ€ ๋…ธํŠธ๋ฅผ ๋“œ๋ž˜๊ทธํ•˜๋Š” ๋™์•ˆ ์ปค์„œ๋ฅผ ๊ฐ–๋Š” ๋ฐฉ์‹๊ณผ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค.

๋ฏธ๋ฆฌ ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค!

question

๊ฐ€์žฅ ์œ ์šฉํ•œ ๋Œ“๊ธ€

๋ˆ„๊ตฌ๋“ ์ง€์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๊นŒ?

๋ชจ๋“  15 ๋Œ“๊ธ€

isDragging ๊ฐ€ true ๋™์•ˆ ์ „์ฒด ๋ณธ๋ฌธ (๋˜๋Š” ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ๋ฃจํŠธ div )์— cursor ์Šคํƒ€์ผ์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. DragLayer ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ isDragging ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„๋“ค์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ํšจ๊ณผ๊ฐ€ ์žˆ์—ˆ์œผ๋ฉดํ•ฉ๋‹ˆ๋‹ค. ๋ถˆํ–‰ํžˆ๋„ ์ปค์„œ ์Šคํƒ€์ผ์„ document.body๋กœ ์„ค์ •ํ•˜๊ณ  "! important"๋กœ ํ‘œ์‹œํ•˜๋”๋ผ๋„ ์ปค์„œ ์Šคํƒ€์ผ์€ ๋ฌด์—‡์ธ๊ฐ€ (๋‚˜๋Š” Chrome์— ์žˆ์Œ)์— ์˜ํ•ด ์žฌ์ •์˜๋ฉ๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

HTML5 ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ API๊ฐ€ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹ ์ผ ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ ์œ ์ผํ•œ ํ•ด๊ฒฐ์ฑ…์€ HTML5 ๋“œ๋ž˜๊ทธ ์•ค ๋“œ๋กญ ์ด๋ฒคํŠธ ๋Œ€์‹  ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ฐฑ์—”๋“œ๋ฅผ ๋งŒ๋“œ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋‚˜๋Š” ๋‹น์‹ ์ด ๊ทธ๋ ‡๊ฒŒ ๋งํ•˜์ง€ ์•Š๊ธฐ๋ฅผ ๋ฐ”๋žฌ์Šต๋‹ˆ๋‹ค :) ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

์ด ๋ฌธ์ œ์— ๋Œ€ํ•ด ๋ฐฉ๊ธˆ ์•Œ๊ฒŒ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ์•ˆํƒ€๊น๊ฒŒ๋„ ๋“œ๋ž˜๊ทธํ•˜๋Š” ๋™์•ˆ ์•„์ด์ฝ˜์„ ๋ณ€๊ฒฝํ•  ์ˆ˜์—†๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

๋ˆ„๊ตฌ๋“ ์ง€์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๊นŒ?

๋˜ํ•œ ๊ฐ€์„œ ์ด๊ฒƒ์„ ์กฐ์‚ฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ, DataTransfer.effectAllowed ์šฉ API๊ฐ€ ๋ช‡ ๊ฐ€์ง€ ์˜ต์…˜ ๋งŒ ์ง€์›ํ•œ๋‹ค๋Š” ์ ์—์„œ ์Šฌํ”„๊ฒŒ๋„ ๋ธŒ๋ผ์šฐ์ € ์ œํ•œ ์‚ฌํ•ญ ์ธ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.

https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/effectAllowed

๋”ฐ๋ผ์„œ์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ @gaearon์ด ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด ๋‹ค๋ฅธ ์‚ฌ์šฉ์ž ์ •์˜ ๋ฐฑ์—”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. / ์ด๊ฒƒ์€ ์‚ฌ๋žŒ๋“ค์—๊ฒŒ ์ง๊ด€์ ์ด์ง€ ์•Š์„ ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ HTML5 ๋ฐฑ์—”๋“œ ๋ฌธ์„œ์— ํฌํ•จ ํ•  ๊ฐ€์น˜๊ฐ€์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งค์šฐ ์ž˜๋ชป๋˜์—ˆ์ง€๋งŒ ์ž‘๋™ํ•˜๋Š” ์†”๋ฃจ์…˜์€ ๋ณธ๋ฌธ์— dragging ํด๋ž˜์Šค๋ฅผ ์ถ”๊ฐ€ํ•˜์—ฌ ๋ณธ๋ฌธ๊ณผ ๋ณธ๋ฌธ ๋‚ด๋ถ€์˜ ๋ชจ๋“  DOM ๊ฐœ์ฒด๊ฐ€ cursor: grabbing !important ์–ป๋„๋กํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ๊ทธ๊ฒƒ์„ ๋ฌด์‹œํ•˜๋Š” ๊ฒƒ ์™ธ์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ์—†์Šต๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•œ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

:global {
    body.dragging,
    body.dragging * {
        cursor: url('./assets/cursors/grabbing.cur'), move !important;
    }
}

JS๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๋˜ ๋‹ค๋ฅธ ์ ‘๊ทผ ๋ฐฉ์‹์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

window.addEventListener('drag', () => {
  document.body.style.cursor = 'grabbing';
}, true)

๊ทธ๋ž˜๋„ ์ผ๊ด€๋˜๊ฒŒ ์ž‘๋™ํ•˜์ง€ ์•Š๋Š” ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค ...

์—ฌ๊ธฐ์—์„œ ์ œ์•ˆ ๋œ ์†”๋ฃจ์…˜์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์— ๋Œ€ํ•œ ์—…๋ฐ์ดํŠธ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

TouchBackend๋กœ ์ „ํ™˜ํ•˜๊ณ  CSS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์„œ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

TouchBackend๋กœ ์ „ํ™˜ํ•˜๊ณ  CSS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์„œ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

๋‚˜๋ฅผ ์œ„ํ•ด ์ผํ•˜์ง€ ์•Š๋Š”๋‹ค

+1

๋ˆ„๊ตฐ๊ฐ€์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๊นŒ?

TouchBackend๋กœ ์ „ํ™˜ํ•˜๊ณ  CSS๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปค์„œ๋ฅผ ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

<DndProvider backend={TouchBackend} options={{ enableTouchEvents: false, enableMouseEvents: true }}>
  <div className=`my-app ${isDragging ? 'dragging' : ''}`>
      .... draglayers here
  </div>
</DndProvider>
.dragging {
  cursor: grabbing
}

TouchBackend๋กœ ์ „ํ™˜ํ•˜๋Š” ๊ฒƒ์ด ์ €์—๊ฒŒ ํšจ๊ณผ์ ์ด์—ˆ์Šต๋‹ˆ๋‹ค.

DnD ๊ณต๊ธ‰์ž :

import { DndProvider } from 'react-dnd';
import { TouchBackend } from 'react-dnd-touch-backend';

...
<DndProvider backend={TouchBackend} options={{enableMouseEvents: true}}>
...
</DndProvider>

๋“œ๋ž˜๊ทธ ํ•ธ๋“ค๋Ÿฌ ๊ตฌํ˜„ :

    import { useDrag, useDrop } from 'react-dnd';
    ....

    const [...] = useDrag({
      ...
      begin: () => {
        document.body.classList.add('dragging');
        ...
     },
      end: () => {
        document.body.classList.remove('dragging');
        ...
      },
    });

css ํŒŒ์ผ

   body.dragging: {
      cursor: crosshair !important;
   },
์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
5 / 5 - 1 ๋“ฑ๊ธ‰