mirror of
https://github.com/penpot/penpot.git
synced 2025-02-18 21:06:11 -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 {
|
li {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
border-bottom: 1px solid $soft-ui-border;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
padding: $small;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.page-icon {
|
.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-x-axis
|
||||||
::grid-color
|
::grid-color
|
||||||
::grid-alignment
|
::grid-alignment
|
||||||
|
::order
|
||||||
::background
|
::background
|
||||||
::background-opacity
|
::background-opacity
|
||||||
::layout]))
|
::layout]))
|
||||||
|
@ -156,7 +157,8 @@
|
||||||
:data {}
|
:data {}
|
||||||
:metadata {:width width
|
:metadata {:width width
|
||||||
:height height
|
:height height
|
||||||
:layout layout}}]
|
:layout layout
|
||||||
|
:order 0}}]
|
||||||
(->> (rp/req :create/page params)
|
(->> (rp/req :create/page params)
|
||||||
(rx/map :payload)
|
(rx/map :payload)
|
||||||
(rx/map page-created)))))
|
(rx/map page-created)))))
|
||||||
|
@ -246,6 +248,22 @@
|
||||||
{:pre [(uuid? id)]}
|
{:pre [(uuid? id)]}
|
||||||
(PersistMetadata. 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
|
;; --- Update Page Options
|
||||||
|
|
||||||
(deftype UpdateMetadata [id metadata]
|
(deftype UpdateMetadata [id metadata]
|
||||||
|
@ -259,6 +277,34 @@
|
||||||
{:pre [(uuid? id) (us/valid? ::metadata metadata)]}
|
{:pre [(uuid? id) (us/valid? ::metadata metadata)]}
|
||||||
(UpdateMetadata. id 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
|
;; --- Update Page
|
||||||
|
|
||||||
(deftype UpdatePage [id name width height layout]
|
(deftype UpdatePage [id name width height layout]
|
||||||
|
|
|
@ -86,21 +86,6 @@
|
||||||
:text i/text
|
:text i/text
|
||||||
:group i/folder))
|
: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)
|
;; --- Shape Name (Component)
|
||||||
|
|
||||||
|
@ -173,7 +158,7 @@
|
||||||
(on-drag-over [event]
|
(on-drag-over [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dnd/set-drop-effect! event "move")
|
(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)))
|
(swap! local assoc :over over)))
|
||||||
(on-drag-enter [event]
|
(on-drag-enter [event]
|
||||||
(swap! local assoc :over true))
|
(swap! local assoc :over true))
|
||||||
|
@ -254,7 +239,7 @@
|
||||||
(on-drag-over [event]
|
(on-drag-over [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dnd/set-drop-effect! event "move")
|
(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)))
|
(swap! local assoc :over over)))
|
||||||
(on-drag-enter [event]
|
(on-drag-enter [event]
|
||||||
(swap! local assoc :over true))
|
(swap! local assoc :over true))
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[uxbox.util.i18n :refer (tr)]
|
[uxbox.util.i18n :refer (tr)]
|
||||||
[uxbox.util.router :as r]
|
[uxbox.util.router :as r]
|
||||||
|
[uxbox.util.data :refer (classnames)]
|
||||||
[potok.core :as ptk]
|
[potok.core :as ptk]
|
||||||
[uxbox.store :as st]
|
[uxbox.store :as st]
|
||||||
[uxbox.main.data.projects :as dp]
|
[uxbox.main.data.projects :as dp]
|
||||||
|
@ -21,6 +22,7 @@
|
||||||
[uxbox.main.ui.icons :as i]
|
[uxbox.main.ui.icons :as i]
|
||||||
[uxbox.util.mixins :as mx :include-macros true]
|
[uxbox.util.mixins :as mx :include-macros true]
|
||||||
[uxbox.main.ui.lightbox :as lbx]
|
[uxbox.main.ui.lightbox :as lbx]
|
||||||
|
[uxbox.util.dom.dnd :as dnd]
|
||||||
[uxbox.util.dom :as dom]))
|
[uxbox.util.dom :as dom]))
|
||||||
|
|
||||||
;; --- Refs
|
;; --- Refs
|
||||||
|
@ -30,7 +32,7 @@
|
||||||
(let [project (get-in state [:workspace :project])]
|
(let [project (get-in state [:workspace :project])]
|
||||||
(->> (vals (:pages state))
|
(->> (vals (:pages state))
|
||||||
(filter #(= project (:project %)))
|
(filter #(= project (:project %)))
|
||||||
(sort-by :created-at))))
|
(sort-by #(get-in % [:metadata :order])))))
|
||||||
|
|
||||||
(def pages-ref
|
(def pages-ref
|
||||||
(-> (l/lens resolve-pages)
|
(-> (l/lens resolve-pages)
|
||||||
|
@ -38,31 +40,84 @@
|
||||||
|
|
||||||
;; --- Component
|
;; --- Component
|
||||||
|
|
||||||
(mx/defc page-item
|
(mx/defcs page-item
|
||||||
{:mixins [(mx/local) mx/static mx/reactive]}
|
{:mixins [(mx/local) mx/static mx/reactive]}
|
||||||
[page total active?]
|
[own page total active?]
|
||||||
(letfn [(on-edit [event]
|
(let [local (:rum/local own)
|
||||||
(udl/open! :page-form {:page page}))
|
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]
|
(on-navigate [event]
|
||||||
(st/emit! (dp/go-to (:project page) (:id page))))
|
(st/emit! (dp/go-to (:project page) (:id page))))
|
||||||
|
|
||||||
(delete []
|
(delete []
|
||||||
(let [next #(st/emit! (dp/go-to (:project page)))]
|
(let [next #(st/emit! (dp/go-to (:project page)))]
|
||||||
(st/emit! (udp/delete-page (:id page) next))))
|
(st/emit! (udp/delete-page (:id page) next))))
|
||||||
|
|
||||||
(on-delete [event]
|
(on-delete [event]
|
||||||
(dom/prevent-default event)
|
(dom/prevent-default event)
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(udl/open! :confirm {:on-accept delete}))]
|
(udl/open! :confirm {:on-accept delete}))
|
||||||
[:li {:class (when active? "selected")
|
|
||||||
:on-click on-navigate}
|
(on-drag-start [event]
|
||||||
[:div.page-icon i/page]
|
(let [target (dom/event->target event)]
|
||||||
[:span (:name page)]
|
(dnd/set-allowed-effect! event "move")
|
||||||
[:div.page-actions
|
(dnd/set-data! event (:id page))
|
||||||
[:a {:on-click on-edit} i/pencil]
|
(dnd/set-image! event target 50 10)
|
||||||
(if (> total 1)
|
(swap! local assoc :dragging true)))
|
||||||
[:a {:on-click on-delete} i/trash])]]))
|
(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
|
(mx/defc sitemap-toolbox
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
{:mixins [mx/static mx/reactive]}
|
{:mixins [mx/static mx/reactive]}
|
||||||
[{:keys [metadata id] :as page}]
|
[{:keys [metadata id] :as page}]
|
||||||
(let [data (merge +page-defaults+
|
(let [data (merge +page-defaults+
|
||||||
(select-keys page [:name :id])
|
(select-keys page [:name :id :project])
|
||||||
(select-keys metadata [:width :height :layout])
|
(select-keys metadata [:width :height :layout])
|
||||||
(mx/react form-data))
|
(mx/react form-data))
|
||||||
valid? (forms/valid? data +page-form+)]
|
valid? (forms/valid? data +page-form+)]
|
||||||
|
|
|
@ -46,3 +46,19 @@
|
||||||
([e key]
|
([e key]
|
||||||
(let [dt (.-dataTransfer e)]
|
(let [dt (.-dataTransfer e)]
|
||||||
(read-string (.getData dt (str key))))))
|
(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