Skip to content

Instantly share code, notes, and snippets.

@dead-claudia
Last active February 27, 2017 16:05
Show Gist options
  • Save dead-claudia/cfac54f77a4cb4ad7131a33e8a8fc315 to your computer and use it in GitHub Desktop.
Save dead-claudia/cfac54f77a4cb4ad7131a33e8a8fc315 to your computer and use it in GitHub Desktop.
Fork of Barney Carroll's Mithril drag component: https://gist.github.com/barneycarroll/decfefa9ed7f21193f55d60436d5d914
import m from 'mithril'
import stream from 'mithril/stream'
import pointerStream from 'pointer-stream'
function targeted(state) {
const node = state._vnode.dom
const {x, y} = state._pointer.coords()
const child = document.elementFromPoint(x, y)
const parent = node.parentNode
if (parent != null) {
if (!parent.contains(child)) return false
if (parent.children.length === state._vnode.domSize) return true
}
for (let i = 0; i < state._vnode.domSize; i++) {
if (node.contains(child)) return true
node = node.nextSibling
}
return false
}
export default {
// Persist vnode data on every draw
oncreate(vnode) { this._vnode = vnode },
onupdate(vnode) { this._vnode = vnode },
oninit(vnode) {
// This allows us to reliably access the last render's dom and attribute data
// Also, create a pointer stream and the drag/drop streams
this._vnode = vnode
this._pointer = pointerStream()
this.drag = stream(false)
this.drop = stream(false)
// Set up the component API streams: drag & drop
if (vnode.attrs.drag) this.drag.map(vnode.attrs.drag)
if (vnode.attrs.drop) this.drop.map(vnode.attrs.drop)
// When its drag status changes…
this._pointer.dragging.map(dragging => {
// We have to have the vdom available first
if (this._vnode.dom == null) return
let redraw = false
if (dragging) {
// We weren't but are now dragging into here
if (!this.drag() && targeted(this)) {
redraw = true
this.drag(true)
}
} else {
// We were but are no longer dragging
if (this.drag()) {
redraw = true
this.drag(false)
}
// We ended a drag and did so here
if (targeted(this)) {
redraw = true
this.drop(true)
}
}
if (redraw) m.redraw()
})
},
// Drag is view agnostic and provides none of its own:
// If a view was supplied as an attribute, we pass the vnode to it so that the state streams can be read from within
// Otherwise we simply render the children: stream data can be read by passing in receiving streams as attributes with corresponding keys
view(vnode) {
return vnode.attrs.view
? vnode.attrs.view.apply(this, arguments)
: vnode.children
},
// Clean up state
onremove({attrs}) {
// Kill the pointer, each API stream, and applicable supplied streams
this._pointer.end(true)
this.drag.end(true)
if (attrs.drag && attrs.drag.end) attrs.drag.end(true)
this.drop.end(true)
if (attrs.drop && attrs.drop.end) attrs.drop.end(true)
},
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment