@micha not a library, but i can show you 🙂
(h/defelem drag-wrap
"A drag and drop wrapper to sit between lists and items."
[{:keys [conn list item dropped-item]} children]
(let [new-item? (j/cell= (= item.data/new-item-id (:db/id item)))
dropped-item (or dropped-item (j/cell nil))
data-transfer-mime "text"
set-dragged-item! (fn [e i]
(-> e
.-originalEvent
.-dataTransfer
(.setData data-transfer-mime (pr-str i))))
event->dragged-item (fn [e]
(-> e
.-originalEvent
.-dataTransfer
(.getData data-transfer-mime)
cljs.reader/read-string))
drag-source? (j/cell false)
drag-target? (j/cell false)
drag-end! #(j/dosync
(reset! drag-source? false)
(reset! drag-target? false)
true)]
(h/div
; Drag source attributes.
; Must be the string "true" as this attribute is enumerated.
; <https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/draggable>
; Don't allow dummy/new items to be draggable.
:draggable (j/cell= (when-not new-item? "true"))
:data-drag-source drag-source?
; todo - replace with real styles.
:garden (j/cell=
(if (not new-item?)
{:cursor :move}
{:cursor :pointer}))
:dragstart #(do (-> % .-originalEvent .-dataTransfer (aset "effectAllowed" "move"))
(set-dragged-item! % @item)
(reset! drag-source? true)
true)
:dragend drag-end!
; Drag target attributes.
:data-drag-target (j/cell= (and drag-target? (not drag-source?)))
:dragenter #(do (.preventDefault %)
(reset! drag-target? true)
false)
:dragover #(do
(-> % .-originalEvent .-dataTransfer (aset "dropEffect" "move"))
(.preventDefault %)
(reset! drag-target? true)
false)
:dragleave #(do (reset! drag-target? false)
false)
:drop #(j/dosync
(.stopPropagation %)
(drag-end!)
(let [; The dropzone item might actually be a new item, in which
; case we just want to move the dropped item to the end of
; the list.
anchor-item (if @new-item?
(or (last @list) @item)
@item)]
(item-list.api/move-item-after! conn @list anchor-item (event->dragged-item %)))
(reset! dropped-item (event->dragged-item %))
false)
children)))
it works OK, but i haven't made a very nice animation/styles for it yet
and also ckeditor is too helpful, if i drag something onto the wysiwyg then it puts the raw dataTransfer data into the editor as EDN
what is the dataTransfer
data? is that part of a built-in drag and drop api?
yeah
dnd api is crazy
wow never even heard of it
you serialise data from the thing being dragged and it gets passed to the thing being dropped onto
thank god for pr-str and read-string in cljs
lol yeah
the nice thing is that you can then drop that serialised data anywhere, including your desktop or other tabs/browser windows
or drop a file from your desktop into the page and receive it as serialised data
not sure how i feel about that
you will definitely experience some emotions working through this api >.<
like i never would imagine that i could just drag stuff from one browser to another
as a user
well you say that, but think of google drive
it's pretty natural in that situation to want to drag things around outside the single browser tab
weird i've never even tried it
well i assume it works, lol
it seems like it should
just seems like kind of a weird thing that is only available on certain webapps
yes definitely
and in order to make it ubiquitous you'd need to basically solve the clipboard problem, a universal serialization of arbitrary things
the general consensus seems to be that the native dnd api is weird and hard to work with >.<
although the extra events are handy
does it work in all browsers?
you get real drag events, rather than needing to try and reverse engineer something from raw mouse events
mostly yeah https://caniuse.com/#feat=dragndrop
yeah those events look pretty handy
looks like there are some polyfills around too
^^ but it's a bit tooo wysiwyg friendly >.<
i think i can fix/disable this behaviour, just looking into it 🙂
:dragover #(do
(-> % .-originalEvent .-dataTransfer (aset "dropEffect" "move"))
(.preventDefault %)
(reset! drag-target? true)
false)
does that send a message when you are dragging an element
like a message to the element you are dragging it over?
ah, yeah so thats an event on the dragged over element
in this context i have a list of things that can be dragged to re-arrange
so i need to put both the "thing being dragged" and "being dragged over/onto" events on all the items
the dataTransfer property has more than just the data in it, in this case i'm actually setting the "dropEffect"
which is literally just what it looks like
non-functional
the preventDefault is what you need to do to enable drag events, because the default behaviour is to disable drag events
hahaha prevent disabling
i like it
:drop #(j/dosync
(.stopPropagation %)
(drag-end!)
(let [; The dropzone item might actually be a new item, in which
; case we just want to move the dropped item to the end of
; the list.
anchor-item (if @new-item?
(or (last @list) @item)
@item)]
(item-list.api/move-item-after! conn @list anchor-item (event->dragged-item %)))
(reset! dropped-item (event->dragged-item %))
false)
this is the end of the dragging, at the drop
that's where i'm actually extracting the data
but you could do that in different ways too, depending on what you're trying to achieve
you probably don't really need to use the dataTransfer stuff
if you're not dragging things across windows
well it's the only way to communicate between the thing being dragged and thing being dragged onto using the events
but you could probably do something with cells too
i mean you can still do what you'd do without the dnd api
yeah
and just use the events themselves
without needing to pass information inside the events
yeah, passing information is the standard integration point
actually it probably makes perfect sense if you wanted to drag an image from your desktop into a wysiwyg
but if you just want a self contained dnd with the events you could just hook some basic cells up
yeah if you're dragging things across windows you need to serialize
or across your app
you can drop onto anything
you might not want to maintain global state cells or many state cells if you have a lot of dnd
it could be easier to just handle the logic of "something just got dropped onto this element" like any other event handler
a flat list of identically structured items is really a special case of what the api can do
it could be handy if you were making a game, where basically anything can be dropped onto anything else
right
it's weird though, because dnd is basically cut and paste
is there some awesome cut and paste api too that i don't know about?
https://developer.mozilla.org/en-US/docs/Web/API/ClipboardEvent this?
i really wouldn't call any of these apis "awesome"...
@micha Will you repost your DnD implementation. Just noticed the Slack log is not logging this channel since mid November 😞 https://clojurians-log.clojureverse.org/hoplon/index.html
What would be the easiest approach to getting drag and drop working with touch/multi-touch. My understanding is that there are significant differences between click events and touch events, and the touch events are not in Jquery by default. Do you generally need a special library or polyfill, etc?
Even if the native browser Api is wierd, would it be advisable to just use, or use raw touch events with Hoplon cells, as discussed above?
Maybe this is a sucessor to hammer.js to add touch events to jQuery https://github.com/benmajor/jQuery-Touch-Events
chromalchemy i think this is micha's gist: https://gist.github.com/micha/4855e90e3085c91768faf150ab0035c3
@alandipert Thx!
interact.js looks pretty slick. http://interactjs.io/ I'm not confident with js interop, so try to stick closely to jquery... any thoughts though?
a couple years ago micha made a touch app w/ hoplon, i think with hammer.js or jq touch. and the integration point was do!, he seemed happy with it iirc
Was that related to this https://github.com/tailrecursion/hoplon.hammerjs/blob/master/src/tailrecursion/hoplon/hammerjs.cljs ? Here it is using on!
yeah we used it in a mobile app we made, it worked pretty well
the dnd thing i made only needs to have some event like mousedown and mouseup, not sure what those are with touch devices
seems like they rejected the draft for touchenter
event
so you'd need to make or find a polyfill to get that functionality