diff --git a/frontend/deps.edn b/frontend/deps.edn index bdbfb515f..db3000d0c 100644 --- a/frontend/deps.edn +++ b/frontend/deps.edn @@ -4,7 +4,7 @@ com.cognitect/transit-cljs {:mvn/version "0.8.256"} funcool/rumext {:git/url "https://github.com/funcool/rumext.git" - :sha "18739f966f12ff7aab3a9bde1fe37a5594f46b8d"} + :sha "ed9bf4c9c19110c6494a0571083a62b3b08eb17b"} cljsjs/react-dom-server {:mvn/version "16.8.6-0"} diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index 6baef2809..0d04fae31 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -50,11 +50,13 @@ (-> (l/lens resolve-project-pages) (l/derive st/state))) +;; DEPRECATED (def selected-page "Ref to the current selected page." (-> (l/lens resolve-page) (l/derive st/state))) +;; DEPRECATED (def selected-page-id "Ref to the current selected page id." (-> (l/key :id) diff --git a/frontend/src/uxbox/main/ui.cljs b/frontend/src/uxbox/main/ui.cljs index 3c852e214..fd4806897 100644 --- a/frontend/src/uxbox/main/ui.cljs +++ b/frontend/src/uxbox/main/ui.cljs @@ -95,7 +95,7 @@ (let [route (mx/react (::route-ref own)) route-id (get-in route [:data :name])] (case route-id - :auth/login (mf/elem auth/login-page) + :auth/login (mf/element auth/login-page) :auth/register (auth/register-page) :auth/recovery-request (auth/recovery-request-page) @@ -106,18 +106,18 @@ (:settings/profile :settings/password :settings/notifications) - (mf/elem settings/settings {:route route}) + (mf/element settings/settings {:route route}) (:dashboard/projects :dashboard/icons :dashboard/images :dashboard/colors) - (mf/elem dashboard/dashboard {:route route}) + (mf/element dashboard/dashboard {:route route}) :workspace/page (let [project (uuid (get-in route [:params :path :project])) page (uuid (get-in route [:params :path :page]))] - (workspace {:project project :page page})) + [:& workspace {:project project :page page}]) nil )))) diff --git a/frontend/src/uxbox/main/ui/auth/login.cljs b/frontend/src/uxbox/main/ui/auth/login.cljs index af2f5cfc2..1d268c305 100644 --- a/frontend/src/uxbox/main/ui/auth/login.cljs +++ b/frontend/src/uxbox/main/ui/auth/login.cljs @@ -99,7 +99,7 @@ (mf/defc login-page [] - (mf/use-effect :end #(st/emit! (fm/clear-form :login))) + (mf/use-effect {:end #(st/emit! (fm/clear-form :login))}) [:div.login [:div.login-body (messages-widget) diff --git a/frontend/src/uxbox/main/ui/dashboard.cljs b/frontend/src/uxbox/main/ui/dashboard.cljs index e2c73a418..64aa47806 100644 --- a/frontend/src/uxbox/main/ui/dashboard.cljs +++ b/frontend/src/uxbox/main/ui/dashboard.cljs @@ -11,12 +11,6 @@ [uxbox.main.ui.dashboard.colors :as colors] [uxbox.main.ui.messages :refer [messages-widget]])) -(def projects-page projects/projects-page) -;; (def elements-page elements/elements-page) -(def icons-page icons/icons-page) -(def images-page images/images-page) -(def colors-page colors/colors-page) - (defn- parse-route [{:keys [params data] :as route}] (let [{:keys [id type]} (:query params) @@ -35,9 +29,9 @@ (let [{:keys [section] :as props} (parse-route route)] [:main.dashboard-main (messages-widget) - (header) ;; TODO: pass section to header + [:& header props] (case section - :dashboard/icons (icons/icons-page props) - :dashboard/images (images/images-page props) - :dashboard/projects (projects/projects-page props) - :dashboard/colors (colors/colors-page props))])) + :dashboard/icons (mf/element icons/icons-page props) + :dashboard/images (mf/element images/images-page props) + :dashboard/projects (mf/element projects/projects-page props) + :dashboard/colors (mf/element colors/colors-page props))])) diff --git a/frontend/src/uxbox/main/ui/dashboard/header.cljs b/frontend/src/uxbox/main/ui/dashboard/header.cljs index d692adb9e..c32d885db 100644 --- a/frontend/src/uxbox/main/ui/dashboard/header.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/header.cljs @@ -18,45 +18,34 @@ [uxbox.util.i18n :refer (tr)] [uxbox.util.router :as rt])) -(mx/defc header-link +(mf/defc header-link [{:keys [section content] :as props}] (let [on-click #(st/emit! (rt/nav section))] [:a {:on-click on-click} content])) -(mf/def header - :mixins [mx/static mx/reactive] - :init - (fn [own props] - (assoc own ::section-ref (-> (l/in [:dashboard :section]) - (l/derive st/state)))) - - :render - (fn [own props] - (let [section (mx/react (::section-ref own)) - projects? (= section :dashboard/projects) - elements? (= section :dashboard/elements) - icons? (= section :dashboard/icons) - images? (= section :dashboard/images) - colors? (= section :dashboard/colors)] +(mf/defc header + [{:keys [section] :as props}] + (let [projects? (= section :dashboard/projects) + icons? (= section :dashboard/icons) + images? (= section :dashboard/images) + colors? (= section :dashboard/colors)] [:header#main-bar.main-bar [:div.main-logo - (header-link {:section :dashboard/projects - :content i/logo})] + [:& header-link {:section :dashboard/projects + :content i/logo}]] [:ul.main-nav [:li {:class (when projects? "current")} - (header-link {:section :dashboard/projects - :content (tr "ds.projects")})] - #_[:li {:class (when elements? "current")} - (header-link :dashboard/elements (tr "ds.elements"))] + [:& header-link {:section :dashboard/projects + :content (tr "ds.projects")}]] [:li {:class (when icons? "current")} - (header-link {:section :dashboard/icons - :content (tr "ds.icons")})] + [:& header-link {:section :dashboard/icons + :content (tr "ds.icons")}]] [:li {:class (when images? "current")} - (header-link {:section :dashboard/images - :content (tr "ds.images")})] + [:& header-link {:section :dashboard/images + :content (tr "ds.images")}]] [:li {:class (when colors? "current")} - (header-link {:section :dashboard/colors - :content (tr "ds.colors")})]] - [:& user]]))) + [:& header-link {:section :dashboard/colors + :content (tr "ds.colors")}]]] + [:& user]])) diff --git a/frontend/src/uxbox/main/ui/dashboard/projects.cljs b/frontend/src/uxbox/main/ui/dashboard/projects.cljs index 0f548f12c..1279b3cea 100644 --- a/frontend/src/uxbox/main/ui/dashboard/projects.cljs +++ b/frontend/src/uxbox/main/ui/dashboard/projects.cljs @@ -38,6 +38,11 @@ (-> (l/in [:dashboard :projects]) (l/derive st/state))) +(def projects-ref + (-> (l/key :projects) + (l/derive st/state))) + + ;; --- Helpers (defn sort-projects-by @@ -110,130 +115,106 @@ ;; --- Grid Item Thumbnail -(mf/def grid-item-thumbnail - :mixins #{mf/memo} - - :init - (fn [own project] - (let [svg (exports/render-page (:page-id project)) - url (some-> svg - (blob/create "image/svg+xml") - (blob/create-uri))] - (assoc own ::url url))) - - :will-unmount - (fn [own] - (let [url (::url own)] - (when url (blob/revoke-uri url)) - own)) - - :render - (fn [own project] - (if-let [url (::url own)] +(mf/defc grid-item-thumbnail + [{:keys [project] :as props}] + (let [url (mf/use-state nil)] + (mf/use-effect + {:watch (:page-id project) + :init (fn [] + (when-let [page-id (:page-id project)] + (let [svg (exports/render-page page-id) + uri (some-> svg + (blob/create "image/svg+xml") + (blob/create-uri))] + (reset! url uri) + uri))) + :end #(when % (blob/revoke-uri %))}) + (if @url [:div.grid-item-th - {:style {:background-image (str "url('" url "')")}}] + {:style {:background-image (str "url('" @url "')")}}] [:div.grid-item-th [:img.img-th {:src "/images/project-placeholder.svg" :alt "Project title"}]]))) + + ;; --- Grid Item -(mf/def grid-item - :key-fn :id - :mixins #{mf/memo (mf/local)} - - :render - (fn [{:keys [::mf/local] :as own} project] - (letfn [(on-navigate [event] - (st/emit! (udp/go-to (:id project)))) - (delete [] - (st/emit! (udp/delete-project project))) - (on-delete [event] - (dom/stop-propagation event) - (udl/open! :confirm {:on-accept delete})) - (on-key-down [event] - (when (kbd/enter? event) - (on-blur event))) - (on-blur [event] - (let [target (dom/event->target event) - name (dom/get-value target) - id (:id project)] - (swap! local assoc :edition false) - (st/emit! (udp/rename-project id name)))) - (on-edit [event] - (dom/stop-propagation event) - (dom/prevent-default event) - (swap! local assoc :edition true))] - [:div.grid-item.project-th {:on-click on-navigate} - (grid-item-thumbnail project) - [:div.item-info - (if (:edition @local) - [:input.element-name {:type "text" - :auto-focus true - :on-key-down on-key-down - :on-blur on-blur - :on-click on-edit - :default-value (:name project)}] - [:h3 (:name project)]) - [:span.date - (str "Updated " (dt/timeago (:modified-at project)))]] - [:div.project-th-actions - [:div.project-th-icon.pages - i/page - [:span (:total-pages project)]] - #_[:div.project-th-icon.comments - i/chat - [:span "0"]] - [:div.project-th-icon.edit - {:on-click on-edit} - i/pencil] - [:div.project-th-icon.delete - {:on-click on-delete} - i/trash]]]))) +(mf/defc grid-item + [{:keys [project] :as props}] + (let [local (mf/use-state {}) + on-navigate #(st/emit! (udp/go-to (:id project))) + delete #(st/emit! (udp/delete-project project)) + on-delete #(do + (dom/stop-propagation %) + (udl/open! :confirm {:on-accept delete})) + on-blur #(let [target (dom/event->target %) + name (dom/get-value target) + id (:id project)] + (swap! local assoc :edition false) + (st/emit! (udp/rename-project id name))) + on-key-down #(when (kbd/enter? %) (on-blur %)) + on-edit #(do + (dom/stop-propagation %) + (dom/prevent-default %) + (swap! local assoc :edition true))] + [:div.grid-item.project-th {:on-click on-navigate} + [:& grid-item-thumbnail {:project project :key (select-keys project [:id :page-id])}] + [:div.item-info + (if (:edition @local) + [:input.element-name {:type "text" + :auto-focus true + :on-key-down on-key-down + :on-blur on-blur + :on-click on-edit + :default-value (:name project)}] + [:h3 (:name project)]) + [:span.date + (str "Updated " (dt/timeago (:modified-at project)))]] + [:div.project-th-actions + [:div.project-th-icon.pages + i/page + [:span (:total-pages project)]] + #_[:div.project-th-icon.comments + i/chat + [:span "0"]] + [:div.project-th-icon.edit + {:on-click on-edit} + i/pencil] + [:div.project-th-icon.delete + {:on-click on-delete} + i/trash]]])) ;; --- Grid -(mf/def grid - :mixins #{mf/memo mf/reactive} - - :init - (fn [own props] - (assoc own ::projects (-> (l/key :projects) - (l/derive st/state)))) - - :render - (fn [own props] - (let [ordering (:order props :created) - filtering (:filter props "") - projects (->> (vals (mf/react (::projects own))) - (filter-projects-by filtering) - (sort-projects-by ordering))] - [:section.dashboard-grid - [:h2 (tr "ds.project-title")] - [:div.dashboard-grid-content - [:div.dashboard-grid-row - [:div.grid-item.add-project {:on-click (fn [e] - (dom/prevent-default e) - (udl/open! :create-project))} - [:span (tr "ds.project-new")]] - (for [item projects] - (grid-item item))]]]))) +(mf/defc grid + {:wrap [mf/reactive*]} + [{:keys [order filter] :or {order :created filter ""} :as props}] + (let [projects (->> (vals (mf/deref projects-ref)) + (filter-projects-by filter) + (sort-projects-by order)) + on-click #(do + (dom/prevent-default %) + (udl/open! :create-project))] + [:section.dashboard-grid + [:h2 (tr "ds.project-title")] + [:div.dashboard-grid-content + [:div.dashboard-grid-row + [:div.grid-item.add-project {:on-click on-click} + [:span (tr "ds.project-new")]] + (for [item projects] + [:& grid-item {:project item :key (:id item)}])]]])) ;; --- Projects Page -(mf/def projects-page - :mixins [mf/memo mf/reactive] - - :init - (fn [own props] - (st/emit! (udp/initialize)) - own) - - :render - (fn [own props] - (let [opts (mf/react opts-ref) - props (merge opts props)] - [:section.dashboard-content - (menu props) - (grid props)]))) +(mf/defc projects-page + {:wrap [mf/reactive*]} + [props] + (let [opts (mf/deref opts-ref) + props (merge opts props)] + (mf/use-effect + {:init #(st/emit! (udp/initialize))}) + [:section.dashboard-content + [:& menu props] + [:& grid props]])) diff --git a/frontend/src/uxbox/main/ui/settings.cljs b/frontend/src/uxbox/main/ui/settings.cljs index 9e45314a4..a86688c39 100644 --- a/frontend/src/uxbox/main/ui/settings.cljs +++ b/frontend/src/uxbox/main/ui/settings.cljs @@ -25,9 +25,9 @@ (messages-widget) [:& header {:section section}] (case section - :settings/profile (mf/elem profile/profile-page) - :settings/password (mf/elem password/password-page) - :settings/notifications (mf/elem notifications/notifications-page))])) + :settings/profile (mf/element profile/profile-page) + :settings/password (mf/element password/password-page) + :settings/notifications (mf/element notifications/notifications-page))])) diff --git a/frontend/src/uxbox/main/ui/settings/header.cljs b/frontend/src/uxbox/main/ui/settings/header.cljs index e811d9dd5..c347c42fc 100644 --- a/frontend/src/uxbox/main/ui/settings/header.cljs +++ b/frontend/src/uxbox/main/ui/settings/header.cljs @@ -13,7 +13,6 @@ [uxbox.main.data.auth :as da] [uxbox.main.data.projects :as dp] [uxbox.main.store :as st] - [uxbox.main.ui.navigation :as nav] [uxbox.main.ui.users :refer [user]] [uxbox.util.i18n :refer [tr]] [uxbox.util.router :as rt])) @@ -24,7 +23,6 @@ [:a {:on-click on-click} content])) (mf/defc header - {:wrap [mf/memo*]} [{:keys [section] :as props}] (let [profile? (= section :settings/profile) password? (= section :settings/password) diff --git a/frontend/src/uxbox/main/ui/workspace.cljs b/frontend/src/uxbox/main/ui/workspace.cljs index 306c9daf4..b9af26044 100644 --- a/frontend/src/uxbox/main/ui/workspace.cljs +++ b/frontend/src/uxbox/main/ui/workspace.cljs @@ -60,12 +60,8 @@ (st/emit! (dw/increase-zoom))) (scroll/scroll-to-point dom mouse-point scroll-position)))) -(def ^:private workspace-page-ref - (-> (l/key :page) - (l/derive refs/workspace))) - (mf/def workspace - :key-fn vector + :key-fn identity :mixins #{mf/memo mf/reactive shortcuts-mixin} @@ -78,7 +74,6 @@ :did-mount (fn [own] (let [{:keys [project page]} (::mf/props own) - ;; dom (mf/ref-node own "workspace-canvas") dom (mf/ref-node (::canvas own)) scroll-to-page-center #(scroll/scroll-to-page-center dom @refs/selected-page) sub (rx/subscribe streams/page-id-ref-s scroll-to-page-center)] @@ -95,7 +90,7 @@ :render (fn [own props] - (let [flags (mf/react refs/flags) + (let [flags (mf/deref refs/flags) project-id (get props :project) page-id (get props :page) left-sidebar? (not (empty? (keep flags [:layers :sitemap @@ -129,7 +124,9 @@ ;; Canvas [:section.workspace-canvas {:id "workspace-canvas" :ref (::canvas own)} - (viewport)]] + [:& viewport {:project project-id + :page page-id + :key [project-id page-id]}]]] ;; Aside (when left-sidebar? diff --git a/frontend/src/uxbox/main/ui/workspace/canvas.cljs b/frontend/src/uxbox/main/ui/workspace/canvas.cljs index e9f5e17aa..c36d48003 100644 --- a/frontend/src/uxbox/main/ui/workspace/canvas.cljs +++ b/frontend/src/uxbox/main/ui/workspace/canvas.cljs @@ -135,20 +135,23 @@ :mixins [mf/reactive] :init - (fn [own props] - (assoc own ::viewport-ref (mf/create-ref))) + (fn [own {:keys [page project] :as props}] + (assoc own + ::viewport (mf/create-ref) + ::page-ref (-> (l/in [:pages page]) + (l/derive st/state)))) :did-mount (fn [own] (letfn [(translate-point-to-viewport [pt] - (let [viewport (mf/ref-node (::viewport-ref own)) + (let [viewport (mf/ref-node (::viewport own)) brect (.getBoundingClientRect viewport) brect (gpt/point (parse-int (.-left brect)) (parse-int (.-top brect)))] (gpt/subtract pt brect))) (translate-point-to-canvas [pt] - (let [viewport (mf/ref-node (::viewport-ref own))] + (let [viewport (mf/ref-node (::viewport own))] (when-let [canvas (dom/get-element-by-class "page-canvas" viewport)] (let [brect (.getBoundingClientRect canvas) bbox (.getBBox canvas) @@ -209,9 +212,11 @@ (events/unlistenByKey (::key3 own)) (dissoc own ::key1 ::key2 ::key3)) + + ;; TODO: use an ad-hoc ref for required keys from workspace state :render (fn [own props] - (let [page (mf/react refs/selected-page) + (let [page (mf/deref (::page-ref own)) flags (mf/react refs/flags) drawing (mf/react refs/selected-drawing-tool) tooltip (or (mf/react refs/selected-tooltip) @@ -263,7 +268,7 @@ (cursor-tooltip tooltip))] [:svg.viewport {:width (* c/viewport-width zoom) :height (* c/viewport-height zoom) - :ref (::viewport-ref own) + :ref (::viewport own) :class (when drawing "drawing") :on-context-menu on-context-menu :on-click on-click diff --git a/frontend/src/uxbox/main/ui/workspace/rules.cljs b/frontend/src/uxbox/main/ui/workspace/rules.cljs index 87589401d..419cf2bc5 100644 --- a/frontend/src/uxbox/main/ui/workspace/rules.cljs +++ b/frontend/src/uxbox/main/ui/workspace/rules.cljs @@ -66,41 +66,37 @@ ;; --- Horizontal Text Label -(mf/def horizontal-text-label - :key-fn second - :render - (fn [own [zoom value]] - (let [big-ticks-mod (big-ticks-mod zoom) - pos (+ (* value zoom) - rule-padding - (* c/canvas-start-x zoom) - c/canvas-scroll-padding)] - (when (< (mod value big-ticks-mod) step-size) - [:text {:x (+ pos 2) - :y 13 - :key (str pos) - :fill "#9da2a6" - :style {:font-size "12px"}} - value])))) +(mf/defc horizontal-text-label + [{:keys [zoom value] :as props}] + (let [big-ticks-mod (big-ticks-mod zoom) + pos (+ (* value zoom) + rule-padding + (* c/canvas-start-x zoom) + c/canvas-scroll-padding)] + (when (< (mod value big-ticks-mod) step-size) + [:text {:x (+ pos 2) + :y 13 + :key (str pos) + :fill "#9da2a6" + :style {:font-size "12px"}} + value]))) ;; --- Horizontal Text Label -(mf/def vertical-text-label - :key-fn second - :render - (fn [own [zoom value]] - (let [big-ticks-mod (big-ticks-mod zoom) - pos (+ (* value zoom) - (* c/canvas-start-x zoom) - c/canvas-scroll-padding)] - (when (< (mod value big-ticks-mod) step-size) - [:text {:y (- pos 3) - :x 5 - :key (str pos) - :fill "#9da2a6" - :transform (str/format "rotate(90 0 %s)" pos) - :style {:font-size "12px"}} - value])))) +(mf/defc vertical-text-label + [{:keys [zoom value] :as props}] + (let [big-ticks-mod (big-ticks-mod zoom) + pos (+ (* value zoom) + (* c/canvas-start-x zoom) + c/canvas-scroll-padding)] + (when (< (mod value big-ticks-mod) step-size) + [:text {:y (- pos 3) + :x 5 + :key (str pos) + :fill "#9da2a6" + :transform (str/format "rotate(90 0 %s)" pos) + :style {:font-size "12px"}} + value]))) ;; --- Horizontal Rule Ticks (Component) @@ -113,7 +109,7 @@ [:g [:path {:d (str/join " " path)}] (for [tick +ticks+] - (horizontal-text-label [zoom tick]))]))) + [:& horizontal-text-label {:zoom zoom :value tick :key tick}])]))) ;; --- Vertical Rule Ticks (Component) @@ -126,7 +122,7 @@ [:g [:path {:d (str/join " " path)}] (for [tick +ticks+] - (vertical-text-label [zoom tick]))]))) + [:& vertical-text-label {:zoom zoom :value tick :key tick}])]))) ;; --- Horizontal Rule (Component) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs index f4fa7feaa..6d3cc60f3 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/sitemap.cljs @@ -25,86 +25,82 @@ [uxbox.util.i18n :refer (tr)] [uxbox.util.router :as r])) -(mf/def page-item - :mixins [(mf/local) mf/memo mf/reactive] - :key-fn :id - - :render - (fn [{:keys [::mf/local] :as own} - {:keys [::deletable? ::selected?] :as page}] - (let [body-classes (classnames - :selected selected? - :drag-active (:dragging @local) - :drag-top (= :top (:over @local)) - :drag-bottom (= :bottom (:over @local)) - :drag-inside (= :middle (:over @local))) - li-classes (classnames +(mf/defc page-item + [{:keys [page deletable? selected?] :as props}] + (let [local (mf/use-state {}) + body-classes (classnames :selected selected? - :hide (:dragging @local))] - (letfn [(on-edit [event] - (udl/open! :page-form {:page page})) + :drag-active (:dragging @local) + :drag-top (= :top (:over @local)) + :drag-bottom (= :bottom (:over @local)) + :drag-inside (= :middle (:over @local))) + li-classes (classnames + :selected selected? + :hide (:dragging @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})) + (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))) - :bottom (let [new-order (inc (get-in page [:metadata :order]))] - (st/emit! (udp/update-order id new-order)))) - (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 li-classes} - [:div.element-list-body - {:class body-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} + (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))) + :bottom (let [new-order (inc (get-in page [:metadata :order]))] + (st/emit! (udp/update-order id new-order)))) + (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 li-classes} + [:div.element-list-body + {:class body-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] - (when deletable? - [:a {:on-click on-delete} i/trash])]]])))) + [:div.page-icon i/page] + [:span (:name page)] + [:div.page-actions {} + [:a {:on-click on-edit} i/pencil] + (when deletable? + [:a {:on-click on-delete} i/trash])]]]))) (mf/def sitemap-toolbox :mixins [mf/memo mf/reactive] @@ -128,4 +124,7 @@ [:ul.element-list (for [page pages] (let [selected? (= (:id page) current-page-id)] - (page-item (assoc page ::deletable? deletable? ::selected? selected?))))]]]))) + [:& page-item {:page page + :deletable? deletable? + :selected? selected? + :key (:id page)}]))]]])))