mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 18:48:37 -05:00
Page ordering
This commit is contained in:
parent
2e04fe60f4
commit
960f2e7e7a
6 changed files with 280 additions and 43 deletions
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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]}
|
||||
|
|
|
@ -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+)]
|
||||
|
|
|
@ -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))))
|
||||
|
|
Loading…
Add table
Reference in a new issue