From 96b3148a02546d5f674471943016e0f6a2593366 Mon Sep 17 00:00:00 2001 From: heyarne Date: Mon, 4 Mar 2019 12:46:36 +0100 Subject: [PATCH] Solve styling problem when sorting table rows --- .../components/sortable/views.cljs | 91 +++++++++++++++---- 1 file changed, 72 insertions(+), 19 deletions(-) diff --git a/src/cljs/airsonic_ui/components/sortable/views.cljs b/src/cljs/airsonic_ui/components/sortable/views.cljs index 188057d..d79b711 100644 --- a/src/cljs/airsonic_ui/components/sortable/views.cljs +++ b/src/cljs/airsonic_ui/components/sortable/views.cljs @@ -1,10 +1,11 @@ (ns airsonic-ui.components.sortable.views (:require [reagent.core :as r] + [clojure.string :as str] ["react-sortable-hoc" :refer [SortableHandle SortableElement SortableContainer]])) (defonce state - (r/atom {:items (vec (map #(str "Item " %) (range 1 51))) + (r/atom {:items (vec (map #(str "Item " %) (range 1 11))) :sort-enabled? true})) ;; this code is taken from https://github.com/reagent-project/reagent/blob/72c95257c13e5de1531e16d1a06da7686041d3f4/examples/react-sortable-hoc/src/example/core.cljs @@ -24,25 +25,23 @@ :userSelect "none"}} "::"])))) -(def SortableItem +(def SortableRow (SortableElement. - (r/reactify-component - (fn [{:keys [value]}] - [:tr - [:td "Hi, I am a table"] - [:td value] - (when (:sort-enabled? @state) - [:td [:> DragHandle]])])))) + (r/reactify-component (fn [{:keys [value]}] + [:tr + [:td value] + (when (:sort-enabled? @state) + [:td [:> DragHandle]])])))) -(def SortableList +(def SortableTable (SortableContainer. (r/reactify-component (fn [{:keys [items]}] - [:table.table + [:table.table.is-fullwidth>tbody (for [[idx value] (map-indexed vector items)] ;; No :> or adapt-react-class here because that would convert value to JS (r/create-element - SortableItem + SortableRow #js {:key (str "item-" idx) :index idx :value value}))])))) @@ -57,11 +56,65 @@ (comment (= [0 2 3 4 1 5] (vector-move [0 1 2 3 4 5] 1 4))) +(defn style-map + "Returns a map representing all currently set css styles; this makes sense + so we can save a non-updating version of it." + [node] + (let [style (js/window.getComputedStyle node)] + (into {} (keep (fn [idx] + (let [property (.item style idx)] + [property (.getPropertyValue style property)])) + (range (.-length style)))))) + +(defn node-seq + "Returns a seq of all of a node's children" + [node] + (loop [waiting [node] + nodes []] + (if-let [node (first waiting)] + (if-let [children (array-seq (.-children node))] + (recur (concat (rest waiting) children) (conj nodes node)) + (recur (rest waiting) (conj nodes node))) + (rest nodes)))) + +(defn style-snapshot + "Recursively grabs the of all of a node's children" + [node] + (into [] (map style-map (node-seq node)))) + +(defn style-from-map! + "Restores the styling saved in a stylemap" + [style-map node] + (let [style (str/join ";" (map (fn [[k v]] (str k ": " v)) style-map))] + (.setAttribute node "style" style))) + +(defn restore-snapshot + "Recursively restores the styling of all of a nodes children" + [style-snapshot node] + (let [nodes (vec (node-seq node))] + (dotimes [i (count nodes)] + (style-from-map! (nth style-snapshot i) (nth nodes i))))) + +(defonce saved-snapshot (atom nil)) + (defn sortable-component [] - (fn [] - (r/create-element - SortableList - #js {:items (:items @state) - :onSortEnd (fn [event] - (swap! state update :items vector-move (.-oldIndex event) (.-newIndex event))) - :useDragHandle true}))) + (r/create-element + SortableTable + #js {:items (:items @state) + :helperClass "sortable-is-moving" + + ;; save the style of all of the rows children + :updateBeforeSortStart + (fn [event] + (reset! saved-snapshot (style-snapshot (.-node event)))) + + :onSortStart + (fn [_] + ;; the node we get passed as parameter is the original node unfortunately + (restore-snapshot @saved-snapshot (js/document.querySelector "body > tr:last-of-type"))) + + ;; update the state to reflect the new order + :onSortEnd + (fn [event] + (swap! state update :items vector-move (.-oldIndex event) (.-newIndex event))) + :useDragHandle true}))