React-dnd: monitor.getClientOffset์„ DropTarget์— ์—ฐ๊ฒฐํ•ด๋„ ์†Œํ’ˆ์ด ์ƒˆ๋กœ ๊ณ ์ณ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

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

DropTarget์„ ๋งŒ๋“ค๊ณ  ๋‹ค์Œ๊ณผ ๊ฐ™์ด getClientOffset์„ ์—ฐ๊ฒฐํ–ˆ์Šต๋‹ˆ๋‹ค.

targetCollect = function (connect, monitor) {
  return {
    connectDropTarget: connect.dropTarget(),
    isOver: monitor.isOver(),
    clientOffset: monitor.getClientOffset()
  }
}

๊ทธ๋Ÿฌ๋‚˜ ๊ตฌ์„ฑ ์š”์†Œ ๋‚ด์—์„œ ๋“œ๋ž˜๊ทธ๋ฅผ ์ด๋™ํ•  ๋•Œ ๋Œ€์ƒ์€ ๋งˆ์šฐ์Šค๋ฅผ ์›€์ง์ผ ๋•Œ๋งˆ๋‹ค ์—…๋ฐ์ดํŠธ ๋œ ์†Œํ’ˆ์„ ์ˆ˜์‹ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค (์ž…๋ ฅ ๋ฐ ์ข…๋ฃŒ์‹œ์—๋งŒ). ๊ทธ๋Ÿฌ๋‚˜ ๋Œ€์ƒ ์‚ฌ์–‘์˜ hover _is_๊ฐ€ ๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. clientOffset prop์ด ๋ณ€๊ฒฝ ๋  ๋•Œ๋งˆ๋‹ค ์ฃผ์ž…๋˜์ง€ ์•Š๋Š” ์ด์œ ๊ฐ€ ์žˆ์Šต๋‹ˆ๊นŒ?

bug wontfix

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

+1. ์ด๊ฒƒ์— ๋ถ€๋”ช ํžˆ๊ณ  getClientOffset ()์ด canDrop ()์—์„œ ์ž˜ ์ž‘๋™ํ•˜๋Š” ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๋ ค๊ณ  ๋งŽ์€ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค-๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค ...-๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๊ฐ€์žˆ๋Š” ํ•ญ๋ชฉ์— ๋Œ€ํ•œ '์–ด๋””'์ œ์–ด๋กœ ์ „๋‹ฌํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹จ์ง€ "์œ„"๊ฐ€ ์•„๋‹ˆ๋ผ ํŠธ๋ฆฌ ๋…ธ๋“œ์˜ ์œ„์™€ ์•„๋ž˜์— ์‚ฝ์ž… ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. canDrop ()์€ ๋…ธ๋“œ๊ฐ€ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ๋งŽ์€ "์œ ํ˜•"์˜ ๋“œ๋กญ ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ๊ตฌ์„ฑ ์š”์†Œ์— ์‹ค์ œ๋กœ ์•Œ๋ ค์„œ ์ปจํŠธ๋กค์ด ๊ทธ์— ๋”ฐ๋ผ ๋‹ค์‹œ ํ‘œ์‹œ๋˜๋„๋ก ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. canDrop์—์„œ ์†Œํ’ˆ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ์–ด์„œ ๋†“๊ธฐ ์ž‘์—… ์ค‘์—๋Š” onMouseMove ๊ตฌ๋…์ด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ง€๊ธˆ๊นŒ์ง€ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„ ๋‚ญ๋น„์˜€์Šต๋‹ˆ๋‹ค ... ์–ด๋–ค ๋„์›€์„ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@ibash , ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•œ ์š”์ ์„ ๊ฒŒ์‹œ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

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

์˜ค, ์ข‹์€ ์ง€์ ์ž…๋‹ˆ๋‹ค. ๋งž์Šต๋‹ˆ๋‹ค. ์ €๋Š”์ด ์‚ฌ์šฉ๋ฒ•์„ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ํ˜„์žฌ ํด๋ผ์ด์–ธํŠธ ์˜คํ”„์…‹ ์ถ”์ ์„ ์„ ํƒํ•˜๋Š” ์œ ์ผํ•œ ๋ฐฉ๋ฒ•์€ DragLayer ์ž…๋‹ˆ๋‹ค. ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์„ฑ๋Šฅ์ƒ์˜ ์ด์œ ๋กœ _drop ๋Œ€์ƒ ์ž์ฒด _์™€ ๊ด€๋ จ๋œ ๊ฒƒ์ด ๋ณ€๊ฒฝ๋œ ๊ฒฝ์šฐ์—๋งŒ props๋ฅผ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

API ๋ฌธ์ œ๋ผ๊ณ  ๋งํ•˜๊ณ  ๋ช‡ ๊ฐ€์ง€ ํ•ด๊ฒฐ์ฑ…์ด์žˆ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

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

๋‚˜๋Š” ๋‘ ๋ฒˆ์งธ ์˜ต์…˜์„ํ•˜๋Š” PR์„ ๋ฐ›์•„ ๋“ค์ผ ๊ฒƒ์ด๋ผ๊ณ  ์ƒ๊ฐํ•œ๋‹ค. DragLayer ๊ตฌํ˜„์„ ํ™•์ธํ•˜์—ฌ ์˜คํ”„์…‹ ๋ณ€๊ฒฝ์„ ๊ตฌ๋…ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‘ ๋ฒˆ์งธ ์˜ต์…˜์„ ์‚ดํŽด ๋ณด์•˜์ง€๋งŒ collect๊ฐ€ ํ•จ์ˆ˜์ด๋ฏ€๋กœ ๊นŒ๋‹ค๋กญ๊ธฐ ๋•Œ๋ฌธ์— ์–ด๋–ค ๋ชจ๋‹ˆํ„ฐ ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ ๋  ์ˆ˜ ์žˆ๋Š”์ง€ ์•Œ์•„๋‚ด๋Š” ๊ฒƒ์ด ๋ถˆ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ์‚ฌ์šฉ ์‚ฌ๋ก€๋Š” ์œ„์น˜์— ์Šค๋ƒ… ๋œ ์œ„์ ฏ์„ ๋“œ๋ž˜๊ทธํ•˜์—ฌ ์ƒ๋Œ€์ ์œผ๋กœ ๋ณต์žกํ•œ ๋ฐฉ์‹์œผ๋กœ ๋‹ค๋ฅธ ์œ„์ ฏ์„ ๋Œ€์ฒด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค (Android์—์„œ ํ”„๋กœ๊ทธ๋žจ ์•„์ด์ฝ˜์„ ๋“œ๋ž˜๊ทธํ•˜๋Š” ๊ฒƒ์„ ์ƒ๊ฐํ•ด๋ณด์‹ญ์‹œ์˜ค). ๋“œ๋ž˜๊ทธ ์ž์ฒด๋Š” HTML5 ์Šค๋ƒ… ์ƒท์œผ๋กœ ์™„๋ฒฝํ•˜๊ฒŒ ๊ดœ์ฐฎ์€ ๋ฐ˜๋ฉด, ์ •ํ™•ํ•œ ํ˜ธ๋ฒ„ ์œ„์น˜์— ๋”ฐ๋ผ ์ž์ฒด์ ์œผ๋กœ ์žฌ์ •๋ ฌํ•ด์•ผํ•˜๋Š” ๊ฒƒ์€ ์‹ค์ œ๋กœ ๋Œ€์ƒ์ž…๋‹ˆ๋‹ค. ๋Œ€์ƒ ๊ตฌ์„ฑ ์š”์†Œ์˜ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋Œ€์ƒ ์‚ฌ์–‘์˜ ํ˜ธ๋ฒ„๋กœ ์ˆ˜ํ–‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ์ง€๊ธˆ์€ ์ด๊ฒƒ์„๋‘๊ณ  ํ•ด๊ฒฐํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค. ๋†€๋ผ์šด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค. ๋‚˜๋Š” ์•„์ฃผ ๋ช‡ ์‹œ๊ฐ„์˜ ์ผ๋กœ ๊ดœ์ฐฎ์€ ์ผ์„ํ•˜๊ณ ์žˆ๋‹ค.

์ง€๊ธˆ์€ ์—ด์–ด ๋‘๊ฒ ์Šต๋‹ˆ๋‹ค. # 172์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. canDrop ๋˜๋Š” ์ˆ˜์ง‘ ๊ธฐ๋Šฅ์—์„œ ๋ธํƒ€๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์‚ฌ๋žŒ๋“ค์„ ์˜ˆ์ƒํ•˜์ง€ ๋ชปํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฌธ์ œ๋ฅผ ๋‹น์žฅ ๊ณ ์น˜์ง€๋Š” ์•Š๊ฒ ์ง€ ๋งŒ ํ•œ ๋‹ฌ ์ •๋„ ์ง€๋‚˜๋ฉด ํ•ด๊ฒฐ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.

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

RxJS-DOM์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์†Œ์˜ ๋“œ๋ž˜๊ทธ ์˜ค๋ฒ„ ์ด๋ฒคํŠธ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ๋งˆ์šฐ์Šค ์ขŒํ‘œ๋กœ ์ƒํƒœ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๊นŒ? ์•„๋งˆ๋„ Hacky ์†”๋ฃจ์…˜.

  componentDidMount() {
    this._events.dragOver = DOM.fromEvent(findDOMNode(this.refs.grid), 'dragover')
                            .throttle(200)
                            .subscribe(this.getMouseCoords)
  }

๋‚˜๋Š” ๊ฒฐ๊ตญ ๋ชจ๋‹ˆํ„ฐ๋ฅผ ์žก๊ณ  DragLayer์™€ ๊ฐ™์€ ์˜คํ”„์…‹ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ตฌ๋…ํ–ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋˜ํ•œ ์žฅ์†Œ์—์„œ ๋‚ด๋ถ€ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋ฅผ ๋‘๋“œ๋ฆฌ๋Š” ๊ฒฐ๊ณผ๋ฅผ ์–ป์—ˆ์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‚ด๊ฐ€ํ•˜๊ณ  ์‹ถ์€ ๊ฒƒ์ด _too_ ๋น„์ •์ƒ์ ์ด๋ผ๊ณ  ์ƒ๊ฐํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ IMO๋Š” ๊ฐ„๋‹จํ•œ API ๋ณ€๊ฒฝ์œผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๊ฒƒ์—์„œ ๋‚˜์˜จ ๊ฒƒ์ด ์žˆ์Šต๋‹ˆ๊นŒ? ์‚ฌ์šฉ์ž ์ •์˜ ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹Œ DropTarget์—์„œ ํ˜ธ๋ฒ„ ์œ„์น˜์— ์•ก์„ธ์Šค ํ•  ์ˆ˜์žˆ์„ ๋•Œ DropTarget์˜ ๊ตฌ์กฐ๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ๊ฒƒ์ด ๋” ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž ์ •์˜ ๋“œ๋ž˜๊ทธ ๋ ˆ์ด์–ด๊ฐ€ ์•„๋‹Œ DropTarget์—์„œ ํด๋ผ์ด์–ธํŠธ ์˜คํ”„์…‹์„ ๋ชจ๋‹ˆํ„ฐ๋งํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

+1 @jchristman์ดํ•˜ ๋ ค๋Š” ์ผ์„ ์ •ํ™•ํžˆํ•˜๊ณ  ์‹ถ์Šต๋‹ˆ๋‹ค.

+1. ์ด๊ฒƒ์— ๋ถ€๋”ช ํžˆ๊ณ  getClientOffset ()์ด canDrop ()์—์„œ ์ž˜ ์ž‘๋™ํ•˜๋Š” ์ด์œ ๋ฅผ ์ดํ•ดํ•˜๋ ค๊ณ  ๋งŽ์€ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค-๋ฐ˜๋ณต์ ์œผ๋กœ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค ...-๊ทธ๋Ÿฌ๋‚˜ ์šฐ๋ฆฌ๊ฐ€์žˆ๋Š” ํ•ญ๋ชฉ์— ๋Œ€ํ•œ '์–ด๋””'์ œ์–ด๋กœ ์ „๋‹ฌํ•  ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. ๋‚˜๋Š” ๋‹จ์ง€ "์œ„"๊ฐ€ ์•„๋‹ˆ๋ผ ํŠธ๋ฆฌ ๋…ธ๋“œ์˜ ์œ„์™€ ์•„๋ž˜์— ์‚ฝ์ž… ํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค. canDrop ()์€ ๋…ธ๋“œ๊ฐ€ ๊ฐ€์ง€๊ณ ์žˆ๋Š” ๋งŽ์€ "์œ ํ˜•"์˜ ๋“œ๋กญ ์ค‘ ์–ด๋–ค ๊ฒƒ์„ ๊ตฌ์„ฑ ์š”์†Œ์— ์‹ค์ œ๋กœ ์•Œ๋ ค์„œ ์ปจํŠธ๋กค์ด ๊ทธ์— ๋”ฐ๋ผ ๋‹ค์‹œ ํ‘œ์‹œ๋˜๋„๋ก ํ•  ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์—†์Šต๋‹ˆ๋‹ค. canDrop์—์„œ ์†Œํ’ˆ์„ ๋ณ€๊ฒฝํ•˜๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ๋Œ์–ด์„œ ๋†“๊ธฐ ์ž‘์—… ์ค‘์—๋Š” onMouseMove ๊ตฌ๋…์ด ์ž‘๋™ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์ง€๊ธˆ๊นŒ์ง€ ์—„์ฒญ๋‚œ ์‹œ๊ฐ„ ๋‚ญ๋น„์˜€์Šต๋‹ˆ๋‹ค ... ์–ด๋–ค ๋„์›€์„ ์ฃผ์‹œ๋ฉด ๊ฐ์‚ฌํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.

@ibash , ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ํ•œ ์š”์ ์„ ๊ฒŒ์‹œ ํ•ด ์ฃผ์‹œ๊ฒ ์Šต๋‹ˆ๊นŒ?

ํ˜ธ๋ฒ„ ์‚ฌ์šฉ์œผ๋กœ ์ „ํ™˜ํ•˜๋ฉด ๋งˆ์นจ๋‚ด ๋‚˜์—๊ฒŒ ๋„์›€์ด๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

`hover : (props : MyProps, ๋ชจ๋‹ˆํ„ฐ : DropTargetMonitor, ๊ตฌ์„ฑ ์š”์†Œ : ReportItemRow) => {
if (๊ตฌ์„ฑ ์š”์†Œ! = null) {
const clientOffset = monitor.getClientOffset ();

        // Is the dragging node dragged above/below us as opposed to "on" us?

        const elementRect = document.elementFromPoint(clientOffset.x, clientOffset.y).getBoundingClientRect();

        const percentageY = (clientOffset.y - elementRect.top) / elementRect.height;

        // Divide the box up into .25 .5 .25
        const insertDragAndDropMagnetPercent = .25;

        if (insertDragAndDropMagnetPercent >= percentageY) {
            component.setState({ dropTarget: "above" });
        }
        else if (1 - insertDragAndDropMagnetPercent >= percentageY) {
            component.setState({ dropTarget: "inside" });
        }
        else {
            component.setState({ dropTarget: "below" });
        }
    }
},`

@ noah79 , ๊ทธ ๋™์•ˆ ๋ฌด์–ธ๊ฐ€๊ฐ€ ์˜ฌ ๋•Œ๊นŒ์ง€ ๊ธฐ๋‹ค๋ฆฌ๋Š” ๋™์•ˆ ์—ฌ๋Ÿฌ ๋“œ๋กญ ํƒ€๊ฒŸ์„ ๋งŒ๋“ค๊ณ  ํŠธ๋ฆฌ ๋…ธ๋“œ ์•„๋ž˜์— ๋‘์–ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ๊ณผ ๊ฐ™์ด :

         _____| <-- drop div1 (above treenode1/below top of list)
treenode1_____| <-- drop div2 (on treenode1)
         _____| <-- drop div3 (below treenode1/above treenode2)
treenode2_____| <-- drop div4 (on treenode 2)
         _____| <-- drop div5 (below treenode2/above bottom of list)

๊ฒฐ๊ตญ 2n + 1 ๋“œ๋กญ ๋Œ€์ƒ์ด๋ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ n์€ ๋ชฉ๋ก์˜ ์š”์†Œ ์ˆ˜์ž…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ๋งˆ์šฐ์Šค๋ฅผ ์˜ฌ๋ ค ๋†“์€ div์— ๋”ฐ๋ผ ํŠธ๋ฆฌ ๋ชฉ๋ก์˜ ๋ชจ์–‘์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ์€ getClientOffset ()์— ์•ก์„ธ์Šค ํ•  ์ˆ˜์—†๋Š” ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ด์™€ ๋งค์šฐ ์œ ์‚ฌํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ–ˆ์ง€๋งŒ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„์ฃผ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๊ฐ ๋“œ๋กญ div์˜ ๋†’์ด๋Š” treenode1 ๋ผ์ธ ๋†’์ด์˜ 1/2์ด์–ด์•ผํ•˜๋ฉฐ ์ฒซ ๋ฒˆ์งธ ๋“œ๋กญ div๋Š” ์ฒซ ๋ฒˆ์งธ ๋ผ์ธ๋ณด๋‹ค ๋†’์€ treenode1 ๋ผ์ธ ๋†’์ด์˜ 1/4์— ์œ„์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค. ์ฆ‰, CSS๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์•„์•ผํ•ฉ๋‹ˆ๋‹ค.

.dropdiv-container {
    position: absolute;
    top: -0.25em; /* If the top of this container is aligned with the top of the treenode list initially */
}
.dropdiv {
    height: 0.5em; /* If the height of treenode1 line is 1em with no padding */
}
<div className='dropdiv-container'>
    { renderDropTargets(2 * listLength + 1) }
</div>

๋ง์ด ๋ผ?

์ด ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐ ํ•œ ๋ฐฉ๋ฒ•์˜ ์š”์ ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํŠธ๋ฆญ์€
react-dnd ๋‚ด๋ถ€ ๋ฐ ๊ธ€๋กœ๋ฒŒ ๋ชจ๋‹ˆํ„ฐ๋ฅผ ์ง์ ‘ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ๋ณด์„ธ์š”
DragDropMonitor.js ์†Œ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๋ฅผ ์–ป์œผ์‹ญ์‹œ์˜ค.
์ฐธ์กฐ : https://github.com/gaearon/dnd-core/blob/master/src/DragDropMonitor.js

๋‹น์‹ ์€ ์ปคํ”ผ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์šฉ์„œํ•ด์•ผ ํ•  ๊ฒƒ์ž…๋‹ˆ๋‹ค :)

  # in the component being dragged, get access to the dragDropManager by adding
  # it to the contextTypes
  #
  # dragDropManager is an instance of this:
  # https://github.com/gaearon/dnd-core/blob/master/src/DragDropManager.js

  <strong i="11">@contextTypes</strong>: {
    dragDropManager: React.PropTypes.object
  }

  # because we can receive events after the component is unmounted, we need to
  # keep track of whether the component is mounted manually.
  #
  # <strong i="12">@_monitor</strong> is what lets us access all the nice internal state - it is an instance of this:
  # https://github.com/gaearon/dnd-core/blob/master/src/DragDropMonitor.js

  componentWillMount: () =>
    <strong i="13">@_isMounted</strong> = true
    <strong i="14">@_monitor</strong> = @context.dragDropManager.getMonitor()

    <strong i="15">@_unsubscribeToStateChange</strong> = @_monitor.subscribeToStateChange(@_onStateChange)
    <strong i="16">@_unsubscribeToOffsetChange</strong> = @_monitor.subscribeToOffsetChange(@_onOffsetChange)

  componentWillUnmount: () =>
    <strong i="17">@_isMounted</strong> = false
    <strong i="18">@_monitor</strong> = null

    @_unsubscribeToStateChange()
    @_unsubscribeToOffsetChange()

  # we can access dragging / dropping state by accessing the monitor 

  _onStateChange: () =>
    return unless <strong i="19">@_isMounted</strong>

    # When we stop dragging reset the counter state and hide all cursors.
    if <strong i="20">@_previousIsDragging</strong> && !@_monitor.isDragging()
      console.log('no longer dragging')
    <strong i="21">@_previousIsDragging</strong> = @_monitor.isDragging()

  _onOffsetChange: () =>
    return unless <strong i="22">@_isMounted</strong>

    # mouse is the x/y coordinates
    mouse = @_monitor.getClientOffset()

    # item is the drag item
    item = @_monitor.getItem()

    # if you want to check if a dragged item is over a target, you need the
    # targetId -- in the DropTarget wrapper you can pass it in like:
    #
    #   (connect, monitor) ->
    #     {
    #       targetId: monitor.targetId,
    #       connectDropTarget: connect.dropTarget()
    #     }
    #
    # and then you can use it like below

    @_monitor.isOverTarget(@props.targetId))

์ด๊ฒŒ ๋ง์ด ๋ผ? ๊ทธ๋ ‡์ง€ ์•Š๋‹ค๋ฉด ๋” ์ž์„ธํ•œ ์ •๋ณด๋ฅผ ์ถ”๊ฐ€ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

๋‚˜๋Š” ๋˜ํ•œ ์˜ค๋Š˜ ์ด๊ฒƒ์„ ์‹คํ–‰ํ•˜๊ณ  ๋ช‡ ์‹œ๊ฐ„์„ ๋‚ญ๋น„ํ–ˆ์Šต๋‹ˆ๋‹ค. DropTarget-> The Collecting Function์˜ ๋ฌธ์„œ์— ๋ฉ”๋ชจ๋ฅผ ๋‚จ๊ธฐ๋Š” ๊ฒƒ์€ ์–ด๋–ป์Šต๋‹ˆ๊นŒ? ๊ทธ๊ฒƒ์€ ์ ์–ด๋„ ์ขŒ์ ˆ๊ฐ์„ ์œ„ํ•ด ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์„ ์•„๋ผ์ง€ ์•Š์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Btw. ์ด ๋ฌธ์ œ์— ๋Œ€ํ•œ ๋˜ ๋‹ค๋ฅธ ์ถ”์•…ํ•œ ํ•ดํ‚น์€ dropTarget.hover() ์—์„œ _as state_ ์ขŒํ‘œ๋ฅผ ๋ณด๋‚ด๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

const dropTarget = {
    hover(props, monitor, component) {
        // HACK! Since there is an open bug in react-dnd, making it impossible
        // to get the current client offset through the collect function as the
        // user moves the mouse, we do this awful hack and set the state (!!)
        // on the component from here outside the component.
        component.setState({
            currentDragOffset: monitor.getClientOffset(),
        });
    },
    drop() { /* ... */ },
};

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

ํŽธ์ง‘ : ์ด๊ฒƒ์€ ๊ธฐ๋ณธ์ ์œผ๋กœ noah79์™€ ๋™์ผํ•ฉ๋‹ˆ๋‹ค. ์ €๋Š” ์ง€๊ธˆ๊นŒ์ง€ ๊ทธ์˜ ์ฝ”๋“œ๋ฅผ ์ฝ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.

๋“œ๋กญ์‹œ ๋งˆ์šฐ์Šค ์œ„์น˜๋ฅผ ์–ป์„ ์ˆ˜์žˆ๋Š” ๋ฐฉ๋ฒ•์ด ์ •๋ง ์—†์Šต๋‹ˆ๊นŒ? ๋‚ด ๋Œ€์ƒ ๊ตฌ์„ฑ ์š”์†Œ๋Š” ๊ทธ๊ฒƒ์— ๋Œ€ํ•ด ์•Œ ํ•„์š”๊ฐ€ ์—†์œผ๋ฏ€๋กœ setState ๋Š” ์˜ต์…˜์ด ์•„๋‹™๋‹ˆ๋‹ค. redux ์ž‘์—…์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๋ ค๋ฉด ์ขŒํ‘œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด ๋งˆ์ง€๋ง‰ ๋ฉ”์‹œ์ง€๋ฅผ ๋ฌด์‹œํ•˜์‹ญ์‹œ์˜ค. ์ด์ œ drop ๋ฐ endDrag ๊ฐ€ ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ๊ธฐ๋Šฅ์ด๋ฉฐ drop ๋‚ด์—์„œ ์ปค์„œ ์œ„์น˜๋ฅผ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด ์˜ˆ์ƒ๋Œ€๋กœ ์ž‘๋™ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ์•Œ์•˜์Šต๋‹ˆ๋‹ค. :)

๋‚ด ํ…Œ์ŠคํŠธ์—์„œ react-dnd๋Š” ํ•ญ๋ชฉ์ด ๋Œ€์ƒ ์œ„๋กœ ๋“œ๋ž˜๊ทธ๋˜๋Š” ์ฆ‰์‹œ ๋งˆ์šฐ์Šค ์ด๋™ ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. html-backend๊ฐ€์ด ์ด๋ฒคํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ๊ฒƒ์ด ๊ฐ€๋Šฅํ•˜๋‹ค๋ฉด, ๋Œ€์ƒ ์ปดํฌ๋„ŒํŠธ๋Š” isOver ์†์„ฑ์ด true๋กœ ์„ค์ • ๋  ๋•Œ ์กฐ๊ฑด๋ถ€๋กœ ์ •์ƒ์ ์ธ mousemove ๋ฆฌ์Šค๋„ˆ๋ฅผ dom์— ๋ฐฐ์น˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ ํ›„์ด ์ฒญ์ทจ์ž๋Š” ๋ฌด์–ธ๊ฐ€๋ฅผ ๋“œ๋ž˜๊ทธ ํ•  ๋•Œ ์ •์ƒ์ ์ธ ๋ฐฉ์‹์œผ๋กœ ๋งˆ์šฐ์Šค ์œ„์น˜๋ฅผ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. drag () ๋ฉ”์„œ๋“œ์—์„œ setState๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ผ์‹œ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋„๋ก ์„ค์ •ํ–ˆ์Šต๋‹ˆ๋‹ค. react๋Š” ๋ Œ๋” ์ „ํ™˜ ์ค‘๊ฐ„์— ์ƒํƒœ ์„ค์ •์— ๋Œ€ํ•œ ๊ฒฝ๊ณ ๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

์ด๊ฒƒ๋„ ์ €๋ฅผ ๋„˜์–ด ๋œจ ๋ ธ์Šต๋‹ˆ๋‹ค. API ์†”๋ฃจ์…˜์— ๋Œ€ํ•œ +1 ๋˜๋Š” ์ ์–ด๋„ FAQ์—์žˆ๋Š” ํ•ญ๋ชฉ.

์ด๋ฅผ ์ง€์›ํ•  ๊ณ„ํš์ด ์žˆ์Šต๋‹ˆ๊นŒ?
ํ•ดํ‚น์—†๋Š” ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์— ๋Œ€ํ•œ ์•„์ด๋””์–ด๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. hover ๋ฅผ ๋ž˜ํ•‘ํ•˜๋Š” DropTarget ๋Œ€ํ•œ ๋ž˜ํผ๋ฅผ ๋งŒ๋“ค์–ด ์ˆ˜์ง‘ ํ•จ์ˆ˜์— ๋Œ€ํ•œ ๋‹ค๋ฅธ ํ˜ธ์ถœ์„ ํŠธ๋ฆฌ๊ฑฐํ•˜๊ณ  ์ˆ˜์ง‘ ํ•จ์ˆ˜๊ฐ€ ์ƒˆ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋‹ค์‹œ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. .

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

์˜คํ”„์…‹์ด ์—…๋ฐ์ดํŠธ๋˜์ง€ ์•Š๋Š” ์ด์œ ๋ฅผ ์กฐ์‚ฌํ•˜๋Š”์ด ๋ฌธ์ œ๋ฅผ ์šฐ์—ฐํžˆ ๋ฐœ๊ฒฌํ–ˆ๋Š”๋ฐ, ์—ฌ์ „ํžˆ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•์ด ํ•„์š”ํ•œ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค.
๋‚˜๋Š” @ jedwards1211 ์˜ ์•„์ด๋””์–ด๋ฅผ DropTarget ์™€ ๊ฑฐ์˜ ๋™์ผํ•œ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์ง€๋งŒ, ์ˆ˜์ง‘ ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ์ฟผ๋ฆฌ ํ•  ์†Œํ’ˆ ๋ชฉ๋ก ์ธ ์˜ต์…˜์—์„œ collectRapidly ๋ฅผ ํ—ˆ์šฉํ•˜๊ณ  ๋ชจ๋“  ํ˜ธ๋ฒ„ ์ด๋ฒคํŠธ์— ๋Œ€ํ•ด ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค. ์ˆ˜์ง‘ ํ•จ์ˆ˜์—์„œ ๋ชจ๋“  ์†Œํ’ˆ์„ ๋ฌด์ฐจ๋ณ„ ์ ์œผ๋กœ ์ „๋‹ฌํ•˜๋ฉด ์•ฝ๊ฐ„์˜ ์ด์ƒ ํ•จ์ด ๋ฐœ์ƒํ•˜๊ณ  connect ๋ฅผ ์ˆ˜์ง‘ ์ž์—๊ฒŒ ์ „ํ˜€ ์ „๋‹ฌํ•  ์ˆ˜ ์—†์œผ๋ฏ€๋กœ connectDropTarget "๋น ๋ฅด๊ฒŒ"์ฟผ๋ฆฌํ•˜์ง€ ์•Š๋„๋ก ๋ณดํ˜ธํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

๋”ฐ๋ผ์„œ DropTarget(types, target, collect) ๋Œ€์‹  RapidFireDropTarget(types, target, collect, {collectRapidly: ['offset']}) . ์—ฌ๊ธฐ์„œ offset ๋Š” ์•„๋งˆ๋„ monitor.getOffset ํ•จ์ˆ˜ ๊ณ„์—ด์—์„œ ๊ฐ’์„๋ฐ›๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

import React from 'react';
import {DropTarget} from 'react-dnd';
import pick from 'lodash/pick';

function RapidFireDropTarget(types, spec, collect, options={}) {
  let collectProps = {};
  const prevHover = spec.hover;
  const {collectRapidly = []} = options;

  const dummyConnect = {
    dropTarget: () => () => {
      throw new Error('Rapidly collected props cannot include results from `connect`');
    }
  };

  const WrappedComponent = Component => {
    return class extends React.Component {
      render() {
        return <Component {...this.props} {...collectProps} />;
      }
    };
  };

  spec.hover = (props, monitor, component) => {
    const allCollectProps = collect(dummyConnect, monitor);

    collectProps = pick(allCollectProps, collectRapidly);
    component.forceUpdate();

    if (prevHover instanceof Function) {
      prevHover(props, monitor, component);
    }
  }

  return (Component) => {
    return DropTarget(types, spec, collect, options)(WrappedComponent(Component));
  };
}

export default RapidFireDropTarget;

์ด ๋ฌธ์ œ๋Š” ์ตœ๊ทผ ํ™œ๋™์ด ์—†์—ˆ๊ธฐ ๋•Œ๋ฌธ์— ์ž๋™์œผ๋กœ ์˜ค๋ž˜๋œ ๊ฒƒ์œผ๋กœ ํ‘œ์‹œ๋˜์—ˆ์Šต๋‹ˆ๋‹ค. ๋” ์ด์ƒ ํ™œ๋™์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์œผ๋ฉด ํ์‡„๋ฉ๋‹ˆ๋‹ค. ๊ท€ํ•˜์˜ ๊ธฐ์—ฌ์— ๊ฐ์‚ฌ๋“œ๋ฆฝ๋‹ˆ๋‹ค.

์ด ํŽ˜์ด์ง€๊ฐ€ ๋„์›€์ด ๋˜์—ˆ๋‚˜์š”?
0 / 5 - 0 ๋“ฑ๊ธ‰