1
0
Fork 0
mirror of https://github.com/heyarne/airsonic-ui.git synced 2026-05-07 10:43:39 +02:00

Make sortable component more reusable

This commit is contained in:
heyarne 2019-03-04 14:13:07 +01:00
commit c715e5025c
4 changed files with 98 additions and 82 deletions

View file

@ -1,16 +1,53 @@
(ns airsonic-ui.components.current-queue.views
(:require [re-frame.core :refer [subscribe]]
[reagent.core :as reagent]
["react-sortable-hoc" :refer [SortableHandle]]
[airsonic-ui.helpers :as helpers]
[airsonic-ui.views.song :as song]
[airsonic-ui.components.sortable.views :as sortable]
[airsonic-ui.routes :as r]))
(defonce items (reagent/atom [1 2 3 4 5 6 7]))
(def SortHandle
(SortableHandle.
;; Alternative to r/reactify-component, which doens't convert props and hiccup,
;; is to just provide fn as component and use as-element or create-element
;; to return React elements from the component.
(fn []
(reagent/as-element [:span {:style {:WebkitTouchCallout "none"
:WebkitUserSelect "none"
:KhtmlUserSelect "none"
:MozUserSelect "none"
;; NOTE: lowercase "ms" prefix
;; https://www.andismith.com/blogs/2012/02/modernizr-prefixed/
:msUserSelect "none"
:userSelect "none"}}
"::"]))))
(defn current-queue []
[:section.section>div.container
[:h1.title "Current Queue"]
[sortable/sortable-component]
(let [current-queue @(subscribe [:audio/current-queue])
#_#_ current-song @(subscribe [:audio/current-song])]
(if (some? current-queue)
[song/listing (:items current-queue)]
[:p "You are currently not playing anything. Use the search or go to your "
[:a {:href (r/url-for ::r/library)} "Library"] " to start playing some music."]))])
(let [is-sortable? (reagent/atom true)]
(fn []
[:section.section>div.container
[:h1.title "Current Queue"]
[sortable/sortable-component {:container [:table.table.is-fullwidth>tbody]
:items @items
:render-item
(fn [{:keys [value]}]
[:tr
[:td "Some table cell"]
[:td (str "Value: " value)]
[:td [:a {:on-click #(swap! is-sortable? not)} (str "Is sortable: " @is-sortable?)]]
[:td (when @is-sortable?
[:> SortHandle])]])
:on-sort-end
(fn [{:keys [old-idx new-idx]}]
(swap! items helpers/vector-move old-idx new-idx))}]
(let [current-queue @(subscribe [:audio/current-queue])
#_#_ current-song @(subscribe [:audio/current-song])]
(if (some? current-queue)
[song/listing (:items current-queue)]
[:p "You are currently not playing anything. Use the search or go to your "
[:a {:href (r/url-for ::r/library)} "Library"] " to start playing some music."]))])))

View file

@ -3,58 +3,21 @@
[clojure.string :as str]
["react-sortable-hoc" :refer [SortableHandle SortableElement
SortableContainer]]))
;; this code is taken and adapted from https://github.com/reagent-project/reagent/blob/72c95257c13e5de1531e16d1a06da7686041d3f4/examples/react-sortable-hoc/src/example/core.cljs
(defonce state
(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
(def DragHandle
(SortableHandle.
;; Alternative to r/reactify-component, which doens't convert props and hiccup,
;; is to just provide fn as component and use as-element or create-element
;; to return React elements from the component.
(fn []
(r/as-element [:span {:style {:WebkitTouchCallout "none"
:WebkitUserSelect "none"
:KhtmlUserSelect "none"
:MozUserSelect "none"
;; NOTE: lowercase "ms" prefix
;; https://www.andismith.com/blogs/2012/02/modernizr-prefixed/
:msUserSelect "none"
:userSelect "none"}}
"::"]))))
(def SortableRow
(SortableElement.
(r/reactify-component (fn [{:keys [value]}]
[:tr
[:td value]
(when (:sort-enabled? @state)
[:td [:> DragHandle]])]))))
(def SortableTable
(SortableContainer.
(r/reactify-component
(fn [{:keys [items]}]
[: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
SortableRow
#js {:key (str "item-" idx)
:index idx
:value value}))]))))
(defn vector-move [coll prev-index new-index]
(let [items (into (subvec coll 0 prev-index)
(subvec coll (inc prev-index)))]
(-> (subvec items 0 new-index)
(conj (get coll prev-index))
(into (subvec items new-index)))))
(comment
(= [0 2 3 4 1 5] (vector-move [0 1 2 3 4 5] 1 4)))
(defn make-wrapper [{:keys [container render-item]}]
(let [SortableItem (SortableElement.
(r/reactify-component render-item))]
(SortableContainer.
(r/reactify-component
(fn [{:keys [items]}]
(into container
(for [[idx value] (map-indexed vector items)]
(r/create-element
SortableItem
#js {:key (str "item-" idx)
:index idx
:value value}))))))))
(defn style-map
"Returns a map representing all currently set css styles; this makes sense
@ -97,24 +60,27 @@
(defonce saved-snapshot (atom nil))
(defn sortable-component []
(r/create-element
SortableTable
#js {:items (:items @state)
:helperClass "sortable-is-moving"
(defn sortable-component [{:keys [container items render-item on-sort-end]}]
(let [Wrapper (make-wrapper {:container container
:render-item render-item})]
(r/create-element
Wrapper
#js {:items items
:helperClass "sortable-is-moving"
;; save the style of all of the rows children
:updateBeforeSortStart
(fn [event]
(reset! saved-snapshot (style-snapshot (.-node event))))
;; 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")))
: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]
(on-sort-end {:old-idx (.-oldIndex event)
:new-idx (.-newIndex event)}))
;; update the state to reflect the new order
:onSortEnd
(fn [event]
(swap! state update :items vector-move (.-oldIndex event) (.-newIndex event)))
:useDragHandle true}))
:useDragHandle true})))