Fork me on GitHub
#hoplon
<
2017-12-15
>
thedavidmeister00:12:44

@micha not a library, but i can show you 🙂

thedavidmeister00:12:47

(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.
   ; 
   ; 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)))

thedavidmeister01:12:39

it works OK, but i haven't made a very nice animation/styles for it yet

thedavidmeister01:12:36

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

micha01:12:40

what is the dataTransfer data? is that part of a built-in drag and drop api?

thedavidmeister01:12:49

dnd api is crazy

micha01:12:00

wow never even heard of it

thedavidmeister01:12:23

you serialise data from the thing being dragged and it gets passed to the thing being dropped onto

thedavidmeister01:12:10

thank god for pr-str and read-string in cljs

thedavidmeister01:12:44

the nice thing is that you can then drop that serialised data anywhere, including your desktop or other tabs/browser windows

thedavidmeister01:12:03

or drop a file from your desktop into the page and receive it as serialised data

micha01:12:32

not sure how i feel about that

thedavidmeister01:12:56

you will definitely experience some emotions working through this api >.<

micha01:12:04

like i never would imagine that i could just drag stuff from one browser to another

micha01:12:07

as a user

thedavidmeister01:12:18

well you say that, but think of google drive

thedavidmeister01:12:52

it's pretty natural in that situation to want to drag things around outside the single browser tab

micha01:12:30

weird i've never even tried it

thedavidmeister01:12:12

well i assume it works, lol

thedavidmeister01:12:15

it seems like it should

micha01:12:19

just seems like kind of a weird thing that is only available on certain webapps

micha01:12:11

and in order to make it ubiquitous you'd need to basically solve the clipboard problem, a universal serialization of arbitrary things

thedavidmeister01:12:43

the general consensus seems to be that the native dnd api is weird and hard to work with >.<

thedavidmeister01:12:51

although the extra events are handy

micha01:12:07

does it work in all browsers?

thedavidmeister01:12:19

you get real drag events, rather than needing to try and reverse engineer something from raw mouse events

micha01:12:09

yeah those events look pretty handy

thedavidmeister01:12:48

looks like there are some polyfills around too

thedavidmeister01:12:37

^^ but it's a bit tooo wysiwyg friendly >.<

thedavidmeister01:12:38

i think i can fix/disable this behaviour, just looking into it 🙂

micha01:12:37

:dragover #(do
               (-> % .-originalEvent .-dataTransfer (aset "dropEffect" "move"))
               (.preventDefault %)
               (reset! drag-target? true)
               false)

micha01:12:17

does that send a message when you are dragging an element

micha01:12:30

like a message to the element you are dragging it over?

thedavidmeister01:12:54

ah, yeah so thats an event on the dragged over element

thedavidmeister01:12:08

in this context i have a list of things that can be dragged to re-arrange

thedavidmeister01:12:31

so i need to put both the "thing being dragged" and "being dragged over/onto" events on all the items

thedavidmeister01:12:13

the dataTransfer property has more than just the data in it, in this case i'm actually setting the "dropEffect"

thedavidmeister01:12:28

which is literally just what it looks like

thedavidmeister01:12:57

the preventDefault is what you need to do to enable drag events, because the default behaviour is to disable drag events

micha01:12:22

hahaha prevent disabling

micha01:12:25

i like it

thedavidmeister01:12:11

: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)

thedavidmeister01:12:56

this is the end of the dragging, at the drop

thedavidmeister01:12:33

that's where i'm actually extracting the data

thedavidmeister01:12:47

but you could do that in different ways too, depending on what you're trying to achieve

micha01:12:25

you probably don't really need to use the dataTransfer stuff

micha01:12:33

if you're not dragging things across windows

thedavidmeister01:12:13

well it's the only way to communicate between the thing being dragged and thing being dragged onto using the events

thedavidmeister01:12:37

but you could probably do something with cells too

micha01:12:51

i mean you can still do what you'd do without the dnd api

micha01:12:01

and just use the events themselves

micha01:12:11

without needing to pass information inside the events

thedavidmeister01:12:40

yeah, passing information is the standard integration point

thedavidmeister01:12:04

actually it probably makes perfect sense if you wanted to drag an image from your desktop into a wysiwyg

thedavidmeister01:12:32

but if you just want a self contained dnd with the events you could just hook some basic cells up

micha01:12:57

yeah if you're dragging things across windows you need to serialize

thedavidmeister01:12:18

or across your app

thedavidmeister01:12:30

you can drop onto anything

thedavidmeister01:12:56

you might not want to maintain global state cells or many state cells if you have a lot of dnd

thedavidmeister01:12:26

it could be easier to just handle the logic of "something just got dropped onto this element" like any other event handler

thedavidmeister01:12:15

a flat list of identically structured items is really a special case of what the api can do

thedavidmeister01:12:16

it could be handy if you were making a game, where basically anything can be dropped onto anything else

micha02:12:21

it's weird though, because dnd is basically cut and paste

micha02:12:38

is there some awesome cut and paste api too that i don't know about?

thedavidmeister02:12:14

i really wouldn't call any of these apis "awesome"...

chromalchemy15:12:16

@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

chromalchemy15:12:50

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?

chromalchemy15:12:54

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?

chromalchemy15:12:44

Maybe this is a sucessor to hammer.js to add touch events to jQuery https://github.com/benmajor/jQuery-Touch-Events

chromalchemy15:12:33

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?

alandipert15:12:03

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

micha15:12:40

yeah we used it in a mobile app we made, it worked pretty well

micha15:12:04

the dnd thing i made only needs to have some event like mousedown and mouseup, not sure what those are with touch devices

micha15:12:58

seems like they rejected the draft for touchenter event

micha15:12:19

so you'd need to make or find a polyfill to get that functionality