Last active
February 7, 2018 00:43
-
-
Save mike-thompson-day8/09b173d52575383841bd1fb5dbbe7ae1 to your computer and use it in GitHub Desktop.
DnD example for re-com
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;; What you are looking at: I just picked up this code from one of our apps, and dumped it here unedited. | |
;; It shows how to do DnD with reagent/re-com, using the terrible HTML5 API. | |
;; I haven't done anything to shrink this code down. It is a straight cut and paste from live code | |
;; so it is complicated by many real world, application issues, sorry. It is also very early | |
;; code for us, on coming to cljs, so it probably contains all sorts of ugly and embarasing attrocities. | |
;; But it does work. | |
;; So, as background, when trying to understand what the code is doing, imagine a panel containing | |
;; a "table" of rows. Each row displays "a single daypart" item (it doesn't matter what a daypart is) | |
;; and there's a button on the left of each row which will popup an editor panel for that daypart | |
;; (row), PLUS the rows can be dragged up and down, so as to confer an ordering. | |
;; View for displaying a daypart row (in a Table) | |
(defn daypart-row | |
[] | |
(let [editor-showing? (reagent/atom false) | |
show-editor #(reset! editor-showing? true) | |
mouse-over-row-fn (fn [mouse-over-id row-id dragging-id] | |
(and | |
(not @dragging-id) | |
(= @mouse-over-id row-id)))] | |
(fn | |
[daypart mouse-over-row-id dragging-id dragging-over-id] | |
(let [this-id (:id daypart) | |
pre-drag-id (:pre-drag-id daypart)] | |
;; DO NOT put in a key. | |
;; If you add one, we don't get on-drag-end events for this combination: | |
;; 1. we drop outside of the table | |
;; 2. having first dragged across the table. | |
;; | |
[h-box | |
:class "rc-div-table-row" | |
:align :center | |
:style {:font-size "small" | |
:cursor #(@editor-showing? "auto" "move") | |
:padding "5px" | |
:line-height "1.428" | |
:vertical-align "middle" | |
:border-top "1px solid #ddd" | |
:background-color (if (= @mouse-over-row-id this-id) "#f5f5f5") ;; bootsrap hover color | |
;; when we are dragging this item, make it disappear, | |
;; so the dragged image doesn't have a noisy background | |
:opacity (if (= @dragging-id this-id) 0) | |
} | |
:attr {:on-mouse-over (handler-fn (reset! mouse-over-row-id this-id) {}) | |
:on-mouse-out (handler-fn (reset! mouse-over-row-id nil) {}) | |
;; Drag and Drop and drop | |
;; Below we use the HTML5 DnD API, which is a flawed and tricky beast: | |
;; - subtle interactions between event handlers. | |
;; - subtle effects related to calling 'preventDefault' in handlers or not doing it. | |
;; Note that for reasons unknown this component can't have a key. See comment further above. | |
;; | |
;; This helped: https://www.refheap.com/73581 | |
;; | |
:draggable (not @editor-showing?) ;; if this is present/true, then browser will allow dragging - don't do it when editor open | |
:on-drag-start (handler-fn | |
(reset! dragging-id this-id) | |
(reset! dragging-over-id this-id) | |
#(.setData (.-dataTransfer event) "text/plain" "blah")) ;; needed for firefox | |
:on-drag-over #(.preventDefault %) ;; without this, won't accept a drag | |
:on-drag-enter (handler-fn | |
(reset! dragging-over-id pre-drag-id) | |
(.preventDefault event)) | |
:on-drop (handler-fn | |
(emit [:dayparts/drag-move @dragging-id @dragging-over-id]) | |
(reset! dragging-over-id nil) | |
(reset! dragging-id nil) | |
(reset! mouse-over-row-id nil)) | |
:on-drag-end (handler-fn ;; if drop happens somewhere impossible, then this event is the only way we have to know that the process has stopped. | |
(reset! dragging-over-id nil) | |
(reset! dragging-id nil) | |
(reset! mouse-over-row-id nil))} | |
:children [[box | |
:width width-edit-column | |
:padding "5px" | |
:justify :center | |
:child [popover-anchor-wrapper | |
:position :left-below | |
:showing? editor-showing? | |
:anchor [row-button | |
:md-icon-name ux-common/md-edit-icon-name | |
:tooltip (if @dragging-id nil "edit") | |
:mouse-over-row? (mouse-over-row-fn mouse-over-row-id this-id dragging-id) | |
:on-click show-editor] | |
:popover [edit-view/popover-wrapper daypart editor-showing? :left-below]] | |
] | |
[label :style {:width width-name-column :text-align "center"} :label (:name daypart) :on-click show-editor] | |
[label :style {:width width-from-column :text-align "center"} :label (mins->time-str (:from daypart)) :on-click show-editor] | |
[label :style {:width width-to-column :text-align "center"} :label (mins->time-str (:to daypart)) :on-click show-editor] | |
[label :style {:width width-days-column :text-align "center"} :label (daypart/bool-mask->umask (:mask daypart)) :on-click show-editor] | |
[box | |
:width width-sort-column | |
:padding "5px" | |
:justify :center | |
:child [row-button | |
:md-icon-name ux-common/md-menu-icon-name ; md-import-export | |
:tooltip (if @dragging-id nil "drag") | |
:mouse-over-row? (mouse-over-row-fn mouse-over-row-id this-id dragging-id) | |
:style {:cursor "move"}]] | |
[h-box | |
:gap "15px" | |
:justify :between | |
:width width-actions-column | |
:children [[row-button | |
:md-icon-name ux-common/md-copy-icon-name | |
:tooltip (if @dragging-id nil "clone") | |
:mouse-over-row? (mouse-over-row-fn mouse-over-row-id this-id dragging-id) | |
:on-click #(emit [:dayparts/duplicate-daypart daypart])] | |
[row-button | |
:md-icon-name ux-common/md-delete-icon-name | |
:tooltip (if @dragging-id nil "delete") | |
:mouse-over-row? (mouse-over-row-fn mouse-over-row-id this-id dragging-id) | |
:on-click #(emit [:dayparts/delete-daypart this-id])]]]]])))) | |
;; OVERALL TABLE | |
(defn dayparts-table | |
[] | |
(let [dragging-id (reagent/atom nil) ;; if user is dragging a daypart, then will be the id of the one being dragged, otherwise nil. | |
dragging-over-id (reagent/atom nil) ;; if not nil, then a daypart has been dragged over the row associated with this daypart | |
mouse-over-row-id (reagent/atom nil)] ;; the mouse is over this row | |
(fn | |
[dayparts] | |
[v-box | |
:children [[gap :size "12px"] | |
[v-box | |
:class "rc-div-table" | |
:style {:border "solid 1px #ddd"} | |
:attr {:on-mouse-out #(reset! mouse-over-row-id nil)} | |
:children [[h-box | |
:class "rc-div-table-header" | |
:style {:font-size "small" :text-align "center"} | |
:children [[label :style {:width width-edit-column} :label "" :width width-edit-column] ;; Doesn't work unless width is repeated | |
[label :style {:width width-name-column} :label "Name"] | |
[label :style {:width width-from-column} :label "From"] | |
[label :style {:width width-to-column} :label "To"] | |
[label :style {:width width-days-column} :label "Days"] | |
[label :style {:width width-sort-column} :label "Sort" :width width-sort-column] ;; Doesn't work unless width is repeated | |
[label :style {:width width-actions-column} :label "Actions"] | |
[gap :size "2px"]]] | |
[v-box | |
:children (for [daypart (drag-reorder @dayparts @dragging-id @dragging-over-id)] | |
[daypart-row daypart mouse-over-row-id dragging-id dragging-over-id])]]] | |
[gap :size "8px"] | |
]]))) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment