Last active
March 14, 2017 20:07
-
-
Save uhop/21fa11bf387e234e0d79bf90d7b08735 to your computer and use it in GitHub Desktop.
The minimalistic DnD code.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
'use strict'; | |
window.dnd = window.dnd || {}; | |
function Move (container, options, node, e) { | |
this.container = container; | |
this.options = options; | |
this.node = node; | |
// handle the state | |
this.node.ownerDocument.body.classList.add('dnd-in-flight'); | |
this.node.classList.add('dnd-dragged'); | |
this.mouseX = e.pageX; | |
this.mouseY = e.pageY; | |
this.avatar = (options.makeAvatar || clone)(this); | |
this.moving = options.moving || moving; | |
this.over = options.over || noop; | |
this.handles = [ | |
on(node.ownerDocument, 'mouseup', this.done.bind(this)), | |
on(node.ownerDocument, 'mousemove', this.move.bind(this)), | |
on(node.ownerDocument, 'dragstart', stopEvent), | |
on(node.ownerDocument.body, 'selectstart', stopEvent) | |
]; | |
(this.options.init || noop)(this); | |
} | |
Move.prototype = { | |
destroy: function () { | |
this.handles.forEach(function (h) { h.remove(); }); | |
this.handles = []; | |
// handle the state | |
this.node.ownerDocument.body.classList.remove('dnd-in-flight'); | |
this.node.classList.remove('dnd-dragged'); | |
if (this.previousOverItem) { | |
this.previousOverItem.classList.remove('dnd-over'); | |
} | |
(this.options.destroy || noop)(this); | |
}, | |
done: function (e) { | |
stopEvent(e); | |
(this.options.drop || noop)(this); | |
this.avatar.parentNode.removeChild(this.avatar); | |
this.destroy(); | |
}, | |
move: function (e) { | |
stopEvent(e); | |
this.moving(this, e); | |
this.mouseX = e.pageX; | |
this.mouseY = e.pageY; | |
this.avatar.style.left = this.x + 'px'; | |
this.avatar.style.top = this.y + 'px'; | |
this.over(this); | |
} | |
}; | |
function start (container, options) { | |
options = options || {}; | |
var callback = process(container, options), | |
filter = options.filter || '.dnd-handle'; | |
return on(container, 'mousedown', filter, callback); | |
} | |
var formNode = {a: 1, input: 1, select: 1, button: 1, textarea: 1, option: 1}; | |
function process (container, options) { | |
return function (e, node) { | |
node = node || e.target; | |
if (!e.button && formNode[node.tagName.toLowerCase()] !== 1 && !node.classList.contains('dnd-ignore')) { | |
new Move(container, options, node, e); | |
stopEvent(e); | |
} | |
}; | |
} | |
function clone (mover) { | |
var node = mover.node, | |
box = node.getBoundingClientRect(), | |
style = window.getComputedStyle(node), | |
avatar = node.cloneNode(true); | |
mover.x = box.left - parseFloat(style.marginLeft) + window.pageXOffset; | |
mover.y = box.top - parseFloat(style.marginTop) + window.pageYOffset; | |
avatar.style.position = 'absolute'; | |
avatar.style.height = style.height; | |
avatar.style.width = style.width; | |
avatar.style.left = mover.x + 'px'; | |
avatar.style.top = mover.y + 'px'; | |
avatar.classList.remove('dnd-dragged'); | |
avatar.classList.add(mover.options.avatarClass || 'dnd-avatar'); | |
node.ownerDocument.body.appendChild(avatar); | |
return avatar; | |
} | |
function moving (mover, e) { | |
mover.x += e.pageX - mover.mouseX; | |
mover.y += e.pageY - mover.mouseY; | |
} | |
function noop (mover) {} | |
function stopEvent (e) { | |
e.preventDefault(); | |
e.stopPropagation(); | |
} | |
dnd.start = start; | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<html> | |
<head> | |
<title>DnD test bed</title> | |
<script src="../bower_components/on/dist/on.js"></script> | |
<script src="../lib/dnd.js"></script> | |
<style> | |
.container { | |
font-size: 18pt; | |
color: black; | |
display: flex; | |
flex-direction: column; | |
max-width: 20em; | |
margin: 2em; | |
border: 3px solid #ddd; | |
padding: 1em; | |
} | |
.item { | |
font-size: 18pt; | |
color: black; | |
cursor: pointer; | |
flex: 1 1 auto; | |
margin: 0.5em; | |
border: 3px solid #ccc; | |
padding: 1em; | |
} | |
.item::selection { | |
background-color: transparent; | |
} | |
.dnd-avatar { | |
background-color: #fcc; | |
opacity: 0.5; | |
} | |
.dnd-dragged { | |
background-color: #cfc; | |
opacity: 0.5; | |
} | |
.dnd-in-flight { | |
background-color: #eee; | |
transition: background-color 0.5s; | |
} | |
.dnd-over { | |
background-color: #ffe; | |
} | |
</style> | |
</head> | |
<body> | |
<h1>DnD test bed</h1> | |
<div class="container"> | |
<div class="item dnd-item dnd-handle">One</div> | |
<div class="item dnd-item dnd-handle">Two</div> | |
<div class="item dnd-item dnd-handle">Three</div> | |
<div class="item dnd-item dnd-handle">Four</div> | |
<div class="item dnd-item dnd-handle">Five</div> | |
</div> | |
<p>Lorem ipsum dolor sit amet, odio consul ea pri, has ridens repudiandae ea, pro ex aliquid accusata. Vix te magna iudicabit, in quot nobis has, mel ex ludus melius ocurreret. Ad homero vidisse quo, soluta possim omittam pri in. Eam adhuc erroribus ex, sed lorem delectus liberavisse ad.</p> | |
<p>Nisl integre scriptorem te has, eam te iisque denique qualisque. Mei laudem aperiam facilisi ut, id altera tritani recteque nam, est id vitae facilisi evertitur. Democritum repudiandae ea ius. Duo cu tritani invenire, ius illum alterum phaedrum eu, nec officiis interesset ei. Mei elit disputationi ad.</p> | |
<p>Te quidam labitur abhorreant usu. Habeo deseruisse ut eum, usu ei unum tractatos. Id est ferri movet nostrum, nullam platonem periculis no quo. Falli vivendum at vis, amet possim moderatius et pri. Salutandi instructior mei ut, augue quidam necessitatibus mel id. Mutat primis qui ex, adipisci quaestio eu duo, at sea wisi causae periculis.</p> | |
<p>Per summo debitis ut. Quem mucius constituam mel ut, eos quodsi animal appetere ex. At doming nostrud vel, inani voluptatum no sed. Et vis exerci vidisse praesent, eam impedit definitionem ea, apeirian vituperatoribus sed no.</p> | |
<p>Nec augue dolor laoreet ad. Ut eos perfecto nominati. Dolorum appellantur id sed, vis probatus constituam appellantur ea. Cu animal alterum molestie has. Tempor praesent tincidunt eu ius, ei oportere posidonium comprehensam pri.</p> | |
<script> | |
dnd.start(document.querySelector('.container'), { | |
init: function (mover) { | |
// container box | |
var rect = mover.container.getBoundingClientRect(), | |
style = window.getComputedStyle(mover.container), | |
box = { | |
top: rect.top + window.pageYOffset + parseFloat(style.borderTopWidth), | |
bottom: rect.bottom + window.pageYOffset - parseFloat(style.borderBottomWidth) - parseFloat(style.marginBottom) | |
}; | |
style = window.getComputedStyle(mover.avatar); | |
box.top -= parseFloat(style.marginTop); | |
box.bottom -= parseFloat(style.marginTop) + parseFloat(style.borderTopWidth) + parseFloat(style.height) + parseFloat(style.borderBottomWidth); | |
mover.containerBox = box; | |
// draggable items | |
var items = mover.container.querySelectorAll('.dnd-item'), itemBoxes = []; | |
for (var i = 0; i < items.length; ++i) { | |
rect = items[i].getBoundingClientRect(); | |
itemBoxes.push({ | |
node: items[i], | |
top: rect.top + window.pageYOffset, | |
bottom: rect.bottom + window.pageYOffset | |
}); | |
} | |
mover.itemBoxes = itemBoxes; | |
}, | |
moving: function (mover, e) { | |
mover.y += e.pageY - mover.mouseY; | |
mover.y = Math.max(mover.containerBox.top, Math.min(mover.containerBox.bottom, mover.y)); | |
}, | |
over: function (mover) { | |
// binary search is better, but linear will do in a pinch | |
this.itemBoxes.some(function (item) { | |
if (item.top <= mover.mouseY && mover.mouseY < item.bottom) { | |
if (item.node !== mover.previousOverItem) { | |
if (mover.previousOverItem) { | |
mover.previousOverItem.classList.remove('dnd-over'); | |
} | |
mover.previousOverItem = item.node; | |
mover.previousOverItem.classList.add('dnd-over'); | |
} | |
return true; | |
} | |
return false; | |
}); | |
}, | |
drop: function (mover) { | |
// binary search is better, but linear will do in a pinch | |
var done = mover.itemBoxes.some(function (item, i, itemBoxes) { | |
if (mover.mouseY < (item.top + item.bottom) / 2) { | |
if (item.node !== mover.node) { | |
mover.container.insertBefore(mover.node, item.node); | |
} | |
return true; | |
} | |
return false; | |
}); | |
if (!done) { | |
mover.container.appendChild(mover.node); | |
} | |
} | |
}); | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment