From 960f2e7e7ae50a18cf57c4c639210d9b0268ca4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Espino?= Date: Wed, 21 Dec 2016 19:17:16 +0100 Subject: [PATCH] Page ordering --- .../styles/main/partials/sidebar-sitemap.scss | 139 +++++++++++++++++- frontend/src/uxbox/main/data/pages.cljs | 48 +++++- .../main/ui/workspace/sidebar/layers.cljs | 19 +-- .../main/ui/workspace/sidebar/sitemap.cljs | 99 ++++++++++--- .../workspace/sidebar/sitemap_pageform.cljs | 2 +- frontend/src/uxbox/util/dom/dnd.cljs | 16 ++ 6 files changed, 280 insertions(+), 43 deletions(-) diff --git a/frontend/resources/styles/main/partials/sidebar-sitemap.scss b/frontend/resources/styles/main/partials/sidebar-sitemap.scss index cb405e154..ca14139f8 100644 --- a/frontend/resources/styles/main/partials/sidebar-sitemap.scss +++ b/frontend/resources/styles/main/partials/sidebar-sitemap.scss @@ -54,10 +54,8 @@ li { align-items: center; - border-bottom: 1px solid $soft-ui-border; display: flex; flex-direction: row; - padding: $small; width: 100%; .page-icon { @@ -147,6 +145,143 @@ } + .element-list-body { + align-items: center; + border-bottom: 1px solid $soft-ui-border; + display: flex; + padding: $small; + transition: none; + width: 100%; + + svg { + fill: $soft-ui-icons; + height: 13px; + margin-right: 8px; + width: 13px; + } + + .element-actions { + align-items: center; + display: flex; + flex-shrink: 0; + width: 62px; + } + + .element-icon { + + svg { + fill: $medium-ui-icons; + } + + } + + .toggle-content { + margin-left: auto; + width: 12px; + + svg { + fill: $intense-ui-icons; + transform: rotate(90deg); + width: 10px; + } + + &.inverse { + svg { transform: rotate(270deg); } + } + + &:hover { + + svg { + fill: $medium-ui-icons; + } + + } + + } + + &.group { + + &.open { + + .toggle-content { + flex-shrink: 0; + + svg { + transform: rotate(270deg); + } + + } + + } + + } + + span.element-name { + min-width: 40px; + min-height: 16px; + display: block; + + color: $medium-ui-text; + font-size: $fs13; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &.selected { + + .element-icon { + + svg { + fill: $main-ui-color; + } + + } + + span { + color: $main-ui-color; + } + + } + + .selected { + + svg { + fill: $intense-ui-icons; + } + + } + + &:hover { + border-color: $main-ui-color; + + .element-icon { + + svg { + fill: $intense-ui-icons; + } + + } + + span { + color: $intense-ui-text; + } + + } + + &.drag-top { + border-top: 40px solid $color-gray-lighter !important; + } + + &.drag-bottom { + border-bottom: 40px solid $color-gray-lighter !important; + } + + &.drag-inside { + border: 2px solid $main-ui-color !important; + } + + } } } diff --git a/frontend/src/uxbox/main/data/pages.cljs b/frontend/src/uxbox/main/data/pages.cljs index 9ac75a529..42ae8d303 100644 --- a/frontend/src/uxbox/main/data/pages.cljs +++ b/frontend/src/uxbox/main/data/pages.cljs @@ -41,6 +41,7 @@ ::grid-x-axis ::grid-color ::grid-alignment + ::order ::background ::background-opacity ::layout])) @@ -156,7 +157,8 @@ :data {} :metadata {:width width :height height - :layout layout}}] + :layout layout + :order 0}}] (->> (rp/req :create/page params) (rx/map :payload) (rx/map page-created))))) @@ -246,6 +248,22 @@ {:pre [(uuid? id)]} (PersistMetadata. id)) +(deftype PersistPages [] + ptk/WatchEvent + (watch [_ state stream] + (letfn [(resolve-pages [state] + (let [project (get-in state [:workspace :project])] + (->> (vals (:pages state)) + (filter #(= project (:project %))) + (sort-by #(get-in % [:metadata :order])))))] + (->> (rx/from-coll (resolve-pages state)) + (rx/map :id) + (rx/map persist-metadata))))) + +(defn persist-pages + [] + (PersistPages.)) + ;; --- Update Page Options (deftype UpdateMetadata [id metadata] @@ -259,6 +277,34 @@ {:pre [(uuid? id) (us/valid? ::metadata metadata)]} (UpdateMetadata. id metadata)) +(deftype UpdateOrder [id order] + IMetadataUpdate + ptk/UpdateEvent + (update [this state] + (assoc-in state [:pages id :metadata :order] order))) + +(defn update-order + [id order] + {:pre [(uuid? id)]} + (UpdateOrder. id order)) + +(deftype ReorderPages [] + IMetadataUpdate + ptk/UpdateEvent + (update [this state] + (letfn [(resolve-pages [] + (let [project (get-in state [:workspace :project])] + (->> (vals (:pages state)) + (filter #(= project (:project %))) + (sort-by #(get-in % [:metadata :order]))))) + (ordered-pages [[state idx] page] + [(assoc-in state [:pages (:id page) :metadata :order] (* 10 idx)) (inc idx)])] + (first (reduce ordered-pages [state, 1] (resolve-pages)))))) + +(defn reorder-pages + [] + (ReorderPages.)) + ;; --- Update Page (deftype UpdatePage [id name width height layout] diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs index 32992ed1c..1680c88bd 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs @@ -86,21 +86,6 @@ :text i/text :group i/folder)) -(defn- get-hover-position - [event group?] - (let [target (.-currentTarget event) - brect (.getBoundingClientRect target) - width (.-offsetHeight target) - y (- (.-clientY event) (.-top brect)) - part (/ (* 30 width) 100)] - (if group? - (cond - (> part y) :top - (< (- width part) y) :bottom - :else :middle) - (if (>= y (/ width 2)) - :bottom - :top)))) ;; --- Shape Name (Component) @@ -173,7 +158,7 @@ (on-drag-over [event] (dom/prevent-default event) (dnd/set-drop-effect! event "move") - (let [over (get-hover-position event false)] + (let [over (dnd/get-hover-position event false)] (swap! local assoc :over over))) (on-drag-enter [event] (swap! local assoc :over true)) @@ -254,7 +239,7 @@ (on-drag-over [event] (dom/prevent-default event) (dnd/set-drop-effect! event "move") - (let [over (get-hover-position event true)] + (let [over (dnd/get-hover-position event true)] (swap! local assoc :over over))) (on-drag-enter [event] (swap! local assoc :over true)) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs index ea5fb7d6f..7687c9c4a 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs @@ -10,6 +10,7 @@ [cuerdas.core :as str] [uxbox.util.i18n :refer (tr)] [uxbox.util.router :as r] + [uxbox.util.data :refer (classnames)] [potok.core :as ptk] [uxbox.store :as st] [uxbox.main.data.projects :as dp] @@ -21,6 +22,7 @@ [uxbox.main.ui.icons :as i] [uxbox.util.mixins :as mx :include-macros true] [uxbox.main.ui.lightbox :as lbx] + [uxbox.util.dom.dnd :as dnd] [uxbox.util.dom :as dom])) ;; --- Refs @@ -30,7 +32,7 @@ (let [project (get-in state [:workspace :project])] (->> (vals (:pages state)) (filter #(= project (:project %))) - (sort-by :created-at)))) + (sort-by #(get-in % [:metadata :order]))))) (def pages-ref (-> (l/lens resolve-pages) @@ -38,31 +40,84 @@ ;; --- Component -(mx/defc page-item +(mx/defcs page-item {:mixins [(mx/local) mx/static mx/reactive]} - [page total active?] - (letfn [(on-edit [event] - (udl/open! :page-form {:page page})) + [own page total active?] + (let [local (:rum/local own) + classes (classnames + :selected active? + :drag-active (:dragging @local) + :drag-top (= :top (:over @local)) + :drag-bottom (= :bottom (:over @local)) + :drag-inside (= :middle (:over @local)))] + (letfn [(on-edit [event] + (udl/open! :page-form {:page page})) - (on-navigate [event] - (st/emit! (dp/go-to (:project page) (:id page)))) + (on-navigate [event] + (st/emit! (dp/go-to (:project page) (:id page)))) - (delete [] - (let [next #(st/emit! (dp/go-to (:project page)))] - (st/emit! (udp/delete-page (:id page) next)))) + (delete [] + (let [next #(st/emit! (dp/go-to (:project page)))] + (st/emit! (udp/delete-page (:id page) next)))) - (on-delete [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (udl/open! :confirm {:on-accept delete}))] - [:li {:class (when active? "selected") - :on-click on-navigate} - [:div.page-icon i/page] - [:span (:name page)] - [:div.page-actions - [:a {:on-click on-edit} i/pencil] - (if (> total 1) - [:a {:on-click on-delete} i/trash])]])) + (on-delete [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (udl/open! :confirm {:on-accept delete})) + + (on-drag-start [event] + (let [target (dom/event->target event)] + (dnd/set-allowed-effect! event "move") + (dnd/set-data! event (:id page)) + (dnd/set-image! event target 50 10) + (swap! local assoc :dragging true))) + (on-drag-end [event] + (swap! local assoc :dragging false :over nil)) + (on-drop [event] + (dom/stop-propagation event) + (let [id (dnd/get-data event) + over (:over @local)] + (case (:over @local) + :top (let [new-order (dec (get-in page [:metadata :order]))] + (st/emit! (udp/update-order id new-order)) + (st/emit! (udp/reorder-pages)) + (st/emit! (udp/persist-pages))) + :bottom (let [new-order (inc (get-in page [:metadata :order]))] + (st/emit! (udp/update-order id new-order)) + (st/emit! (udp/reorder-pages)) + (st/emit! (udp/persist-pages)))) + (swap! local assoc :dragging false :over nil))) + (on-drag-over [event] + (dom/prevent-default event) + (dnd/set-drop-effect! event "move") + (let [over (dnd/get-hover-position event false)] + (swap! local assoc :over over))) + (on-drag-enter [event] + (swap! local assoc :over true)) + (on-drag-leave [event] + (swap! local assoc :over false))] + [:li {:class (when active? "selected")} + [:div.element-list-body + {:class classes + :style {:opacity (if (:dragging @local) + "0.5" + "1")} + :on-click on-navigate + :on-double-click #(dom/stop-propagation %) + :on-drag-start on-drag-start + :on-drag-enter on-drag-enter + :on-drag-leave on-drag-leave + :on-drag-over on-drag-over + :on-drag-end on-drag-end + :on-drop on-drop + :draggable true} + + [:div.page-icon i/page] + [:span (:name page)] + [:div.page-actions + [:a {:on-click on-edit} i/pencil] + (if (> total 1) + [:a {:on-click on-delete} i/trash])]]]))) (mx/defc sitemap-toolbox {:mixins [mx/static mx/reactive]} diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs index b8c305dae..3c2c660bb 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap_pageform.cljs @@ -59,7 +59,7 @@ {:mixins [mx/static mx/reactive]} [{:keys [metadata id] :as page}] (let [data (merge +page-defaults+ - (select-keys page [:name :id]) + (select-keys page [:name :id :project]) (select-keys metadata [:width :height :layout]) (mx/react form-data)) valid? (forms/valid? data +page-form+)] diff --git a/frontend/src/uxbox/util/dom/dnd.cljs b/frontend/src/uxbox/util/dom/dnd.cljs index 28e51dad6..8d27930d7 100644 --- a/frontend/src/uxbox/util/dom/dnd.cljs +++ b/frontend/src/uxbox/util/dom/dnd.cljs @@ -46,3 +46,19 @@ ([e key] (let [dt (.-dataTransfer e)] (read-string (.getData dt (str key)))))) + +(defn get-hover-position + [event group?] + (let [target (.-currentTarget event) + brect (.getBoundingClientRect target) + width (.-offsetHeight target) + y (- (.-clientY event) (.-top brect)) + part (/ (* 30 width) 100)] + (if group? + (cond + (> part y) :top + (< (- width part) y) :bottom + :else :middle) + (if (>= y (/ width 2)) + :bottom + :top))))