Skip to content

Instantly share code, notes, and snippets.

@fre-sch
Created March 12, 2018 06:48
Show Gist options
  • Save fre-sch/147868083b18d2991484a45ae839faa8 to your computer and use it in GitHub Desktop.
Save fre-sch/147868083b18d2991484a45ae839faa8 to your computer and use it in GitHub Desktop.
preact drag and drop
import {h, Component} from "preact"
const lorem = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."
class DndListItem extends Component {
render({onDragStart, children}) {
return (
<div class="dnd-list-item"
draggable="true"
onDragStart={onDragStart}>
{children}
</div>
)
}
}
class DndList extends Component {
onDragStart = (index, ev) => {
ev.dataTransfer.setData("text", index)
}
hit(r, p) {
return (
p.x >= r.x && p.x < r.x + r.width
&& p.y >= r.y && p.y < r.y + r.height
)
}
onDrop = (ev) => {
ev.preventDefault()
let draggedIdx = parseInt(ev.dataTransfer.getData("text"))
let pos = {x: ev.clientX, y: ev.clientY}
let children = Array.from(this.base.querySelectorAll(".dnd-list-item"))
let insertBeforeIdx = null
for (let idx=0, n=children.length; idx < n; ++idx) {
let childRect = children[idx].getBoundingClientRect()
if (this.hit(childRect, pos)) {
this.props.onMoveItem(draggedIdx, idx)
return
}
// "remember" the first child that's below the cursor position
if (insertBeforeIdx == null && childRect.y >= pos.y) {
insertBeforeIdx = idx
}
}
this.props.onMoveItem(draggedIdx, insertBeforeIdx)
}
render({children}) {
return (
<div class="dnd-list"
onDrop={this.onDrop}
onDragOver={ev => ev.preventDefault()}>
{children.map((it, index) => (
<DndListItem onDragStart={
(...args) => this.onDragStart(index, ...args)
}>{it}</DndListItem>
))}
</div>
)
}
}
export default class App extends Component {
state = {
items: [
{title: "First Item", body: lorem},
{title: "Second Item", body: lorem},
{title: "Third Item", body: lorem},
{title: "Fourth Item", body: lorem},
{title: "Fifth Item", body: lorem},
]
}
onMoveItem = (source, dest) => {
let {items} = this.state
let sourceItem = items.splice(source, 1)
items.splice(dest, 0, ...sourceItem)
this.setState({items})
}
render({}, {items}) {
return (
<div style="width:500px;margin:0 auto">
<DndList onMoveItem={this.onMoveItem}>
{items.map(it => (
<div style="box-shadow:0 1px 2px rgba(0,0,0,0.4);margin-bottom:0.5em;background:#fff">
<div style="padding:5px;font-weight:bold;border-bottom:1px solid #ddd">{it.title}</div>
<div style="padding:5px">{it.body}</div>
</div>
))}
</DndList>
</div>
)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment