Skip to content

Instantly share code, notes, and snippets.

@micha
Last active November 30, 2017 16:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save micha/4855e90e3085c91768faf150ab0035c3 to your computer and use it in GitHub Desktop.
Save micha/4855e90e3085c91768faf150ab0035c3 to your computer and use it in GitHub Desktop.
Hoplon drag-n-drop demo.
(ns dnd.core
(:require
[hoplon.core :as h :refer [defelem on!]]
[javelin.core :as j :refer [with-let cell cell= deref*]]))
(declare end!)
(defn start!
"Start dragging the item at position <pos>."
[this pos]
(let [pos (deref* pos)
{:keys [set-event-handler! on-start]} @this]
@set-event-handler!
(swap! this merge {:start-pos pos :current-pos pos})
(on-start pos)))
(defn drag-to!
"Move the item currently being dragged to the new position <new-pos>."
[this new-pos]
(let [new-pos (deref* new-pos)
{:keys [current-pos reorder-fn! on-drag]} @this]
(when current-pos
(reorder-fn! current-pos new-pos)
(swap! this assoc :current-pos new-pos)
(on-drag current-pos new-pos))))
(defn end!
"Stop dragging."
[this]
(let [{:keys [start-pos current-pos on-end]} @this]
(when current-pos
(swap! this merge {:start-pos nil :current-pos nil})
(on-end start-pos current-pos))))
(defn make-dnd
"Create a new drag-n-drop state machine. The <reorder-fn!> is a function that
takes two arguments, <from> and <to>, and does whatever needs to be done to
move the item at position <from> to be at the new position <to>.
Three event handlers may be specified:
:on-start (fn [start-pos] ...) ;; Fired when the user starts to drag an item.
:on-drag (fn [prev-pos curr-pos] ...) ;; Fired when the item is dragged to a new position.
:on-end (fn [start-pos end-pos] ...) ;; Fired when the item is dropped to its final position."
[reorder-fn! & {:keys [on-start on-drag on-end]}]
(with-let [this (cell {:start-pos nil
:current-pos nil
:reorder-fn! reorder-fn!
:on-start (or on-start (fn [_]))
:on-drag (or on-drag (fn [_ _]))
:on-end (or on-end (fn [_ _]))})]
(swap! this assoc :set-event-handler!
(delay (on! (.-documentElement js/document) :mouseup #(end! this)) true))))
(defelem draggable-item
"Add drag-n-drop functionality to an item."
[{:keys [dnd pos] :as attr} [elem]]
(elem
:mousedown #(start! dnd pos)
:mouseenter #(drag-to! dnd pos)
:class (cell= {:dragging-this (= pos (:current-pos dnd))
:dragging-other (and (:current-pos dnd) (not= pos (:current-pos dnd)))})))
(page "index.html"
(:require [dnd.core :as dnd]))
(defc items
[{:name "foo1" :price "1.00"}
{:name "foo2" :price "2.00"}
{:name "foo3" :price "3.00"}
{:name "foo4" :price "4.00"}
{:name "foo5" :price "5.00"}
{:name "foo6" :price "6.00"}])
(defc= items-indexed
(map-indexed vector items))
(defn vec-move
"Given a vector <coll>, moves the item at index <from> to be at index <to>."
[coll from to]
(let [v1 (into (subvec coll 0 from) (subvec coll (inc from)))]
(into (conj (subvec v1 0 to) (get coll from)) (subvec v1 to))))
(def dnd-machine
(dnd/make-dnd
#(swap! items vec-move %1 %2)
:on-start #(pr :on-start %1)
:on-drag #(pr :on-drag %1 %2)
:on-end #(pr :on-end %1 %2)))
(html
(head
(style
"body > div { width: 600px; margin: 0 auto; }
h1 { text-align: center; }
ul { cursor: grabbing; cursor: -moz-grabbing; cursor: -webkit-grabbing; list-style-type: none; padding: 0; margin: 0; background: #6c6c6c; border: 1px solid black; }
li { padding: 10px; background: white; border: 1px solid black; }
li.draggable { cursor: move; cursor: grab; cursor: -moz-grab; cursor: -webkit-grab; user-select: none; }
li.dragging-other { cursor: grabbing; cursor: -moz-grabbing; cursor: -webkit-grabbing; background: #efefef; }
li.dragging-this { cursor: grabbing; cursor: -moz-grabbing; cursor: -webkit-grabbing; transform: rotate(0.75deg); box-shadow: 0px 0px 10px #6c6c6c; }"))
(body
(div
(h1 (code "DRAG AND DROP"))
(ul (for-tpl [[idx {:keys [name price] :as item}] items-indexed]
(dnd/draggable-item :dnd dnd-machine :pos idx
(li :class "draggable" (cell= (str name ": " price)))))))))
@micha
Copy link
Author

micha commented Nov 30, 2017

recorded

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment