d-draggable-table
Version:
Derby draggable table component based on derby sink.
126 lines (109 loc) • 4.15 kB
text/coffeescript
module.exports = class SortableTable
name: 'sortable-table'
create: ->
= null
.addListener 'mousemove', document, (e) =>
.addListener 'mouseup', document, =>
.addListener 'blur', window, =>
onRowDown: (e) ->
e, cloneRow, setTop, breakTop, '.row', finishRow, e.target.parentNode
onColDown: (e) ->
e, cloneCol, setLeft, breakLeft, '.col', finishCol, e.target
onMove: (e) ->
return unless
{parent, last, breakFn} =
loc = .setFn e
i = 0
children = parent.querySelectorAll .selector
i++ while loc > breakFn(
if i < last then children[i] else children[i + 1]
)
unless i == last
unless ref = children[if i < last then i else i + 1]
ref = children[children.length - 1].nextSibling
parent.insertBefore .clone, ref
.last = i
endDragging: (cancel) ->
return unless
.finish cancel
document.body.removeChild .container
= null
dragStart: (e, cloneFn, setFn, breakFn, selector, finish, el) ->
return if e.button != 0
e.preventDefault?()
container = document.createElement 'table'
container.style.position = 'absolute'
parent = el.parentNode
rect = el.getBoundingClientRect()
nodeOffset = targetIndex(parent.querySelector selector)
index = targetIndex(el) - nodeOffset
clone = cloneFn container, el, rect, parent, nodeOffset, index
offsetLeft = rect.left - e.clientX
offsetTop = rect.top - e.clientY
= {component: this, el, parent, clone, nodeOffset, index, last: index, setFn, breakFn, selector, offsetLeft, offsetTop, finish, container}
setLeft.call , e
setTop.call , e
document.body.appendChild container
targetIndex = (el) ->
for child, i in el.parentNode.childNodes
return i if child == el
setLeft = (e) ->
loc = e.clientX
.style.left = (loc + window.pageXOffset + ) + 'px'
return loc
setTop = (e) ->
loc = e.clientY
.style.top = (loc + window.pageYOffset + ) + 'px'
return loc
breakLeft = (el) ->
return unless el
rect = el.getBoundingClientRect()
rect.width / 2 + rect.left
breakTop = (el) ->
return unless el
rect = el.getBoundingClientRect()
rect.height / 2 + rect.top
cloneRow = (container, el, rect, parent) ->
spacerHtml = '<tr>'
for child in el.children
spacerHtml += "<td style=width:#{child.offsetWidth}px;height:0;padding:0>"
clone = el.cloneNode(false)
clone.removeAttribute 'id'
clone.style.height = rect.height + 'px'
container.innerHTML = clone.innerHTML = spacerHtml
parent.insertBefore clone, el
container.firstChild.appendChild el
return clone
cloneCol = (container, el, rect, parent, nodeOffset, index) ->
rows = parent.parentNode.children
spacerHtml = ''
for row in rows
spacerHtml += "<tr class=#{row.className} style=height:#{row.offsetHeight}px;width:0;padding:0>"
container.innerHTML = spacerHtml
clone = el.cloneNode(false)
clone.removeAttribute 'id'
clone.setAttribute 'rowspan', rows.length
clone.style.padding = 0
clone.style.width = rect.width + 'px'
nodeIndex = index + nodeOffset
parent.insertBefore clone, parent.childNodes[nodeIndex + 1]
cloneRows = container.firstChild.children
for row, i in rows
cloneRows[i].appendChild row.childNodes[nodeIndex]
return clone
finishRow = (cancel) ->
# Put things back where they started
.removeChild
.insertBefore , .childNodes[ + ]
# Actually do the move
.emit 'rowMove', , unless cancel
finishCol = (cancel) ->
# Put things back where they started
.removeChild
rows = .parentNode.children
cloneRows = .firstChild.children
nodeIndex = +
for row, i in rows
row.insertBefore cloneRows[i].firstChild, row.childNodes[nodeIndex]
# Actually do the move
.emit 'colMove', , unless cancel