From c354c560d4fcd1c73b79358555e91aabe10c8798 Mon Sep 17 00:00:00 2001 From: Andrew Zhurov Date: Mon, 25 Jul 2022 15:27:30 +0300 Subject: [PATCH 01/12] :tada: Add shape outline on hover upon layers in the left sidebar of workspace Signed-off-by: Andrew Zhurov --- frontend/src/app/main/data/workspace.cljs | 5 ++++ .../app/main/data/workspace/highlight.cljs | 28 +++++++++++++++++++ frontend/src/app/main/refs.cljs | 3 ++ .../app/main/ui/workspace/sidebar/layers.cljs | 12 ++++++++ .../src/app/main/ui/workspace/viewport.cljs | 2 ++ .../main/ui/workspace/viewport/outline.cljs | 23 +++++++++------ 6 files changed, 65 insertions(+), 8 deletions(-) create mode 100644 frontend/src/app/main/data/workspace/highlight.cljs diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 1b734551c..451180143 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -45,6 +45,7 @@ [app.main.data.workspace.path.shapes-to-path :as dwps] [app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.selection :as dws] + [app.main.data.workspace.highlight :as dwh] [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] @@ -1732,6 +1733,10 @@ (dm/export dws/select-shape) (dm/export dws/shift-select-shapes) +;; Highlight +(dm/export dwh/highlight-shape) +(dm/export dwh/dehighlight-shape) + ;; Groups (dm/export dwg/mask-group) (dm/export dwg/unmask-group) diff --git a/frontend/src/app/main/data/workspace/highlight.cljs b/frontend/src/app/main/data/workspace/highlight.cljs new file mode 100644 index 000000000..3968295c7 --- /dev/null +++ b/frontend/src/app/main/data/workspace/highlight.cljs @@ -0,0 +1,28 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) UXBOX Labs SL + +(ns app.main.data.workspace.highlight + (:require + [app.common.spec :as us] + [potok.core :as ptk])) + +;; --- Manage shape's highlight status + +(defn highlight-shape + [id] + (us/verify ::us/uuid id) + (ptk/reify ::highlight-shape + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace-local :highlighted] clojure.set/union #{id})))) + +(defn dehighlight-shape + [id] + (us/verify ::us/uuid id) + (ptk/reify ::dehighlight-shape + ptk/UpdateEvent + (update [_ state] + (update-in state [:workspace-local :highlighted] disj id)))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index ce4f0b8d5..650c648d9 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -117,6 +117,9 @@ (wsh/process-selected-shapes objects selected)) selected-shapes-data)) +(def highlighted-shapes + (l/derived :highlighted workspace-local)) + (defn make-selected-ref [id] (l/derived #(contains? % id) selected-shapes)) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 05ad9bdfd..46c69b657 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -144,6 +144,16 @@ :else (st/emit! (dw/select-shape id))))) + on-pointer-enter + (fn [event] + (let [id (:id item)] + (st/emit! (dw/highlight-shape id)))) + + on-pointer-leave + (fn [event] + (let [id (:id item)] + (st/emit! (dw/dehighlight-shape id)))) + on-context-menu (fn [event] (dom/prevent-default event) @@ -217,6 +227,8 @@ [:div.element-list-body {:class (dom/classnames :selected selected? :icon-layer (= (:type item) :icon)) :on-click select-shape + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave :on-double-click #(dom/stop-propagation %)} [:& si/element-icon {:shape item}] [:& layer-name {:shape item diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 3dfbe8cb8..d808c7cc5 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -70,6 +70,7 @@ drawing (mf/deref refs/workspace-drawing) options (mf/deref refs/workspace-page-options) focus (mf/deref refs/workspace-focus-selected) + highlighted (mf/deref refs/highlighted-shapes) objects-ref (mf/use-memo #(refs/workspace-page-objects-by-id page-id)) objects (mf/deref objects-ref) @@ -286,6 +287,7 @@ {:objects base-objects :selected selected :hover #{(:id @hover) @frame-hover} + :highlighted highlighted :edition edition :zoom zoom}]) diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index d1f6eaf46..731fb192c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -78,8 +78,9 @@ (mf/defc shape-outlines {::mf/wrap-props false} [props] - (let [selected (or (obj/get props "selected") #{}) - hover (or (obj/get props "hover") #{}) + (let [selected (or (obj/get props "selected") #{}) + hover (or (obj/get props "hover") #{}) + highlighted (or (obj/get props "highlighted") #{}) objects (obj/get props "objects") edition (obj/get props "edition") @@ -89,12 +90,18 @@ show-outline? (fn [shape] (and (not (:hidden shape)) (not (:blocked shape)))) - shapes (->> outlines-ids - (filter #(not= edition %)) - (map #(get objects %)) - (filterv show-outline?) - (filter some?))] - + shapes (set + (into + (->> outlines-ids + (filter #(not= edition %)) + (map #(get objects %)) + (filterv show-outline?) + (filter some?)) + ;; outline highlighted shapes even if they are hidden or blocked + (->> highlighted + (filter #(not= edition %)) + (map #(get objects %)) + (filter some?))))] [:g.outlines [:& shape-outlines-render {:shapes shapes :zoom zoom}]])) From 1fa25060a06acd2b8b5948f783c4304ec55d1739 Mon Sep 17 00:00:00 2001 From: Andrew Zhurov Date: Mon, 25 Jul 2022 15:42:17 +0300 Subject: [PATCH 02/12] :tada: Add shape outline on hover upon layers in workspace contextual menu Signed-off-by: Andrew Zhurov --- .../app/main/ui/workspace/context_menu.cljs | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index 11003df3a..ee2378b15 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -38,7 +38,7 @@ (dom/stop-propagation event)) (mf/defc menu-entry - [{:keys [title shortcut on-click children selected? icon] :as props}] + [{:keys [title shortcut on-click on-pointer-enter on-pointer-leave on-unmount children selected? icon] :as props}] (let [submenu-ref (mf/use-ref nil) hovering? (mf/use-ref false) @@ -48,7 +48,8 @@ (mf/set-ref-val! hovering? true) (let [submenu-node (mf/ref-val submenu-ref)] (when (some? submenu-node) - (dom/set-css-property! submenu-node "display" "block"))))) + (dom/set-css-property! submenu-node "display" "block"))) + (when on-pointer-enter (on-pointer-enter)))) on-pointer-leave (mf/use-callback @@ -59,7 +60,8 @@ (timers/schedule 200 #(when-not (mf/ref-val hovering?) - (dom/set-css-property! submenu-node "display" "none"))))))) + (dom/set-css-property! submenu-node "display" "none"))))) + (when on-pointer-leave (on-pointer-leave)))) set-dom-node (mf/use-callback @@ -68,6 +70,10 @@ (when (and (some? dom) (some? submenu-node)) (dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))] + (when on-unmount + (mf/use-effect + (fn [] on-unmount))) + (if icon [:li.icon-menu-item {:ref set-dom-node :on-click on-click @@ -128,7 +134,10 @@ do-bring-to-front #(st/emit! (dw/vertical-order-selected :top)) do-send-backward #(st/emit! (dw/vertical-order-selected :down)) do-send-to-back #(st/emit! (dw/vertical-order-selected :bottom)) - select-shapes (fn [id] #(st/emit! (dws/select-shape id)))] + select-shapes (fn [id] #(st/emit! (dws/select-shape id))) + on-pointer-enter (fn [id] #(st/emit! (dw/highlight-shape id))) + on-pointer-leave (fn [id] #(st/emit! (dw/dehighlight-shape id))) + on-unmount (fn [id] #(st/emit! (dw/dehighlight-shape id)))] [:* (when (> (count hover-objs) 1) [:& menu-entry {:title (tr "workspace.shape.menu.select-layer")} @@ -136,6 +145,9 @@ [:& menu-entry {:title (:name object) :selected? (some #(= object %) shapes) :on-click (select-shapes (:id object)) + :on-pointer-enter (on-pointer-enter (:id object)) + :on-pointer-leave (on-pointer-leave (:id object)) + :on-unmount (on-unmount (:id object)) :icon (si/element-icon {:shape object})}])]) [:& menu-entry {:title (tr "workspace.shape.menu.forward") :shortcut (sc/get-tooltip :bring-forward) From 5834e29b39fb217cae71210e6f04778fc82e1b18 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 08:26:02 +0200 Subject: [PATCH 03/12] :fire: Remove unnecesary ref, viewport already has access to all local data --- frontend/src/app/main/refs.cljs | 3 --- frontend/src/app/main/ui/workspace/viewport.cljs | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 650c648d9..ce4f0b8d5 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -117,9 +117,6 @@ (wsh/process-selected-shapes objects selected)) selected-shapes-data)) -(def highlighted-shapes - (l/derived :highlighted workspace-local)) - (defn make-selected-ref [id] (l/derived #(contains? % id) selected-shapes)) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index d808c7cc5..3a1b6e05a 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -53,6 +53,7 @@ panning selrect transform + highlighted vbox vport zoom @@ -70,7 +71,6 @@ drawing (mf/deref refs/workspace-drawing) options (mf/deref refs/workspace-page-options) focus (mf/deref refs/workspace-focus-selected) - highlighted (mf/deref refs/highlighted-shapes) objects-ref (mf/use-memo #(refs/workspace-page-objects-by-id page-id)) objects (mf/deref objects-ref) From 1477837cbf6273b6e4c098b92d4daf9053ae00c5 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 08:30:00 +0200 Subject: [PATCH 04/12] :lipstick: Use concat-vec helper instead of set + into --- .../main/ui/workspace/viewport/outline.cljs | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index 731fb192c..d35722e96 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -8,6 +8,7 @@ (:require [app.common.exceptions :as ex] [app.common.geom.shapes :as gsh] + [app.common.data :as d] [app.util.object :as obj] [app.util.path.format :as upf] [clojure.set :as set] @@ -90,18 +91,17 @@ show-outline? (fn [shape] (and (not (:hidden shape)) (not (:blocked shape)))) - shapes (set - (into - (->> outlines-ids - (filter #(not= edition %)) - (map #(get objects %)) - (filterv show-outline?) - (filter some?)) - ;; outline highlighted shapes even if they are hidden or blocked - (->> highlighted - (filter #(not= edition %)) - (map #(get objects %)) - (filter some?))))] + shapes (d/concat-set + (->> outlines-ids + (filter #(not= edition %)) + (map #(get objects %)) + (filterv show-outline?) + (filter some?)) + ;; outline highlighted shapes even if they are hidden or blocked + (->> highlighted + (filter #(not= edition %)) + (map #(get objects %)) + (filter some?)))] [:g.outlines [:& shape-outlines-render {:shapes shapes :zoom zoom}]])) From c3f67e63588cae766ebd17ded873b0057d109b65 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 08:32:32 +0200 Subject: [PATCH 05/12] :lipstick: Many cosmetic and indentation changes on outlines component --- .../main/ui/workspace/viewport/outline.cljs | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index d35722e96..ca2b126c7 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -76,32 +76,34 @@ :zoom zoom :color color}]))) +(defn- show-outline? + [shape] + (and (not (:hidden shape)) + (not (:blocked shape)))) + (mf/defc shape-outlines {::mf/wrap-props false} [props] - (let [selected (or (obj/get props "selected") #{}) - hover (or (obj/get props "hover") #{}) - highlighted (or (obj/get props "highlighted") #{}) + (let [selected (or (obj/get props "selected") #{}) + hover (or (obj/get props "hover") #{}) + highlighted (or (obj/get props "highlighted") #{}) - objects (obj/get props "objects") - edition (obj/get props "edition") - zoom (obj/get props "zoom") + objects (obj/get props "objects") + edition (obj/get props "edition") + zoom (obj/get props "zoom") - outlines-ids (set/union selected hover) - show-outline? (fn [shape] (and (not (:hidden shape)) - (not (:blocked shape)))) + outlines (set/union selected hover) - shapes (d/concat-set - (->> outlines-ids - (filter #(not= edition %)) - (map #(get objects %)) - (filterv show-outline?) - (filter some?)) - ;; outline highlighted shapes even if they are hidden or blocked - (->> highlighted - (filter #(not= edition %)) - (map #(get objects %)) - (filter some?)))] + shapes (d/concat-set + (->> outlines + (filter #(not= edition %)) + (map #(get objects %)) + (filterv show-outline?) + (filter some?)) + ;; outline highlighted shapes even if they are hidden or blocked + (->> highlighted + (filter #(not= edition %)) + (map #(get objects %)) + (filter some?)))] [:g.outlines - [:& shape-outlines-render {:shapes shapes - :zoom zoom}]])) + [:& shape-outlines-render {:shapes shapes :zoom zoom}]])) From b5796b4cdb090338e0ac7a2aeaeb21f252ed6992 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 08:38:40 +0200 Subject: [PATCH 06/12] :zap: Efficiency improvements on outlines component --- .../main/ui/workspace/viewport/outline.cljs | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index ca2b126c7..17bd7734b 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -92,18 +92,17 @@ edition (obj/get props "edition") zoom (obj/get props "zoom") - outlines (set/union selected hover) + lookup (d/getf objects) + edition? (fn [o] (= edition o)) + + shapes (-> #{} + (into (comp (remove edition?) + (keep lookup) + (filter show-outline?)) + (set/union selected hover)) + (into (comp (remove edition?) + (keep lookup)) + highlighted))] - shapes (d/concat-set - (->> outlines - (filter #(not= edition %)) - (map #(get objects %)) - (filterv show-outline?) - (filter some?)) - ;; outline highlighted shapes even if they are hidden or blocked - (->> highlighted - (filter #(not= edition %)) - (map #(get objects %)) - (filter some?)))] [:g.outlines [:& shape-outlines-render {:shapes shapes :zoom zoom}]])) From d2aa985714e358af675420f44fad12c0848f4d36 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 08:52:42 +0200 Subject: [PATCH 07/12] :sparkles: Properly use use-fn hook on layer-item component on sidebar --- .../app/main/ui/workspace/sidebar/layers.cljs | 183 ++++++++++-------- 1 file changed, 97 insertions(+), 86 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 46c69b657..a780c566a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -84,101 +84,114 @@ (:name shape "") (when (seq (:touched shape)) " *")]))) -(defn- make-collapsed-iref - [id] - #(-> (l/in [:expanded id]) - (l/derived refs/workspace-local))) - (mf/defc layer-item [{:keys [index item selected objects] :as props}] (let [id (:id item) - selected? (contains? selected id) - container? (or (cph/frame-shape? item) - (cph/group-shape? item)) + blocked? (:blocked item) + hidden? (:hidden item) disable-drag (mf/use-state false) scroll-to-middle? (mf/use-var true) + expanded-iref (mf/with-memo [id] + (-> (l/in [:expanded id]) + (l/derived refs/workspace-local))) - expanded-iref (mf/use-memo - (mf/deps id) - (make-collapsed-iref id)) - - expanded? (mf/deref expanded-iref) + expanded? (mf/deref expanded-iref) + selected? (contains? selected id) + container? (or (cph/frame-shape? item) + (cph/group-shape? item)) toggle-collapse - (fn [event] - (dom/stop-propagation event) - (if (and expanded? (kbd/shift? event)) - (st/emit! (dwc/collapse-all)) - (st/emit! (dwc/toggle-collapse id)))) + (mf/use-fn + (mf/deps expanded?) + (fn [event] + (dom/stop-propagation event) + (if (and expanded? (kbd/shift? event)) + (st/emit! (dwc/collapse-all)) + (st/emit! (dwc/toggle-collapse id))))) toggle-blocking - (fn [event] - (dom/stop-propagation event) - (if (:blocked item) - (st/emit! (dw/update-shape-flags [id] {:blocked false})) - (st/emit! (dw/update-shape-flags [id] {:blocked true}) - (dw/deselect-shape id)))) + (mf/use-fn + (mf/deps id blocked?) + (fn [event] + (dom/stop-propagation event) + (if blocked? + (st/emit! (dw/update-shape-flags [id] {:blocked false})) + (st/emit! (dw/update-shape-flags [id] {:blocked true}) + (dw/deselect-shape id))))) toggle-visibility - (fn [event] - (dom/stop-propagation event) - (if (:hidden item) - (st/emit! (dw/update-shape-flags [id] {:hidden false})) - (st/emit! (dw/update-shape-flags [id] {:hidden true})))) + (mf/use-fn + (mf/deps hidden?) + (fn [event] + (dom/stop-propagation event) + (if hidden? + (st/emit! (dw/update-shape-flags [id] {:hidden false})) + (st/emit! (dw/update-shape-flags [id] {:hidden true}))))) select-shape - (fn [event] - (dom/prevent-default event) - (reset! scroll-to-middle? false) - (let [id (:id item)] - (cond - (kbd/shift? event) - (st/emit! (dw/shift-select-shapes id)) + (mf/use-fn + (mf/deps id) + (fn [event] + (dom/prevent-default event) + (reset! scroll-to-middle? false) + (cond + (kbd/shift? event) + (st/emit! (dw/shift-select-shapes id)) - (kbd/mod? event) - (st/emit! (dw/select-shape id true)) + (kbd/mod? event) + (st/emit! (dw/select-shape id true)) - (> (count selected) 1) - (st/emit! (dw/select-shape id)) - :else - (st/emit! (dw/select-shape id))))) + (> (count selected) 1) + (st/emit! (dw/select-shape id)) + + :else + (st/emit! (dw/select-shape id))))) on-pointer-enter - (fn [event] - (let [id (:id item)] - (st/emit! (dw/highlight-shape id)))) + (mf/use-fn + (mf/deps id) + (fn [event] + (st/emit! (dw/highlight-shape id)))) on-pointer-leave - (fn [event] - (let [id (:id item)] - (st/emit! (dw/dehighlight-shape id)))) + (mf/use-fn + (mf/deps id) + (fn [event] + (st/emit! (dw/dehighlight-shape id)))) on-context-menu - (fn [event] - (dom/prevent-default event) - (dom/stop-propagation event) - (let [pos (dom/get-client-position event)] - (st/emit! (dw/show-shape-context-menu {:position pos - :shape item})))) + (mf/use-fn + (mf/deps item) + (fn [event] + (dom/prevent-default event) + (dom/stop-propagation event) + (let [pos (dom/get-client-position event)] + (st/emit! (dw/show-shape-context-menu {:position pos :shape item}))))) on-drag - (fn [{:keys [id]}] - (when (not (contains? selected id)) - (st/emit! (dw/select-shape id)))) + (mf/use-fn + (mf/deps id selected) + (fn [{:keys [id]}] + (when (not (contains? selected id)) + (st/emit! (dw/select-shape id))))) on-drop - (fn [side _data] - (if (= side :center) - (st/emit! (dw/relocate-selected-shapes (:id item) 0)) - (let [to-index (if (= side :top) (inc index) index) - parent-id (cph/get-parent-id objects (:id item))] - (st/emit! (dw/relocate-selected-shapes parent-id to-index))))) + (mf/use-fn + (mf/deps id) + (fn [side _data] + (if (= side :center) + (st/emit! (dw/relocate-selected-shapes id 0)) + (let [to-index (if (= side :top) (inc index) index) + parent-id (cph/get-parent-id objects id)] + (st/emit! (dw/relocate-selected-shapes parent-id to-index)))))) on-hold - (fn [] - (when-not expanded? - (st/emit! (dwc/toggle-collapse (:id item))))) + (mf/use-fn + (mf/deps id expanded?) + (fn [] + (when-not expanded? + (st/emit! (dwc/toggle-collapse id))))) [dprops dref] (hooks/use-sortable :data-type "penpot/layer" @@ -193,25 +206,23 @@ ref (mf/use-ref)] - (mf/use-effect - (mf/deps selected? selected) - (fn [] - (let [single? (= (count selected) 1) - node (mf/ref-val ref) + (mf/with-effect [selected? selected] + (let [single? (= (count selected) 1) + node (mf/ref-val ref) - subid - (when (and single? selected?) - (let [scroll-to @scroll-to-middle?] - (ts/schedule - 100 - #(if scroll-to - (dom/scroll-into-view! node #js {:block "center", :behavior "smooth"}) - (do - (dom/scroll-into-view-if-needed! node #js {:block "center", :behavior "smooth"}) - (reset! scroll-to-middle? true))))))] + subid + (when (and single? selected?) + (let [scroll-to @scroll-to-middle?] + (ts/schedule + 100 + #(if scroll-to + (dom/scroll-into-view! node #js {:block "center", :behavior "smooth"}) + (do + (dom/scroll-into-view-if-needed! node #js {:block "center", :behavior "smooth"}) + (reset! scroll-to-middle? true))))))] - #(when (some? subid) - (rx/dispose! subid))))) + #(when (some? subid) + (rx/dispose! subid)))) [:li {:on-context-menu on-context-menu :ref dref @@ -385,7 +396,7 @@ (and (:show-search-box @filter-state) (or (d/not-empty? (:search-text @filter-state)) - (d/not-empty? (:active-filters @filter-state)))) + (d/not-empty? (:active-filters @filter-state)))) search-and-filters (fn [[id shape]] @@ -438,7 +449,7 @@ [filtered-objects handle-show-more - + (mf/html (if (:show-search-box @filter-state) [:* @@ -499,7 +510,7 @@ (fn [entries] (when (and (.-isIntersecting (first entries)) (some? show-more)) (show-more))) - + on-render-container (fn [element] (let [options #js {:root element} From 9263f70d6af1ce2db2217d644176f46ef147a4c4 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 09:36:52 +0200 Subject: [PATCH 08/12] :sparkles: Use properly the react hook We can't use hooks in a condition, the condition should be inside the hook. --- frontend/src/app/main/ui/workspace/context_menu.cljs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index ee2378b15..e010e0c56 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -70,9 +70,10 @@ (when (and (some? dom) (some? submenu-node)) (dom/set-css-property! submenu-node "top" (str (.-offsetTop dom) "px"))))))] - (when on-unmount - (mf/use-effect - (fn [] on-unmount))) + + (mf/use-effect + (mf/deps on-unmount) + (constantly on-unmount)) (if icon [:li.icon-menu-item {:ref set-dom-node From a48db277b9cb14c177e3390afc0641d85ee94035 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 09:37:50 +0200 Subject: [PATCH 09/12] :sparkles: Avoid recursive rerender and react warning --- .../app/main/ui/workspace/context_menu.cljs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index e010e0c56..e77e8c0ba 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -8,6 +8,7 @@ "A workspace specific context menu (mouse right click)." (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.pages.helpers :as cph] [app.common.types.page :as ctp] [app.main.data.events :as ev] @@ -130,20 +131,29 @@ [:& menu-separator]])) (mf/defc context-menu-layer-position - [{:keys [hover-objs shapes]}] - (let [do-bring-forward #(st/emit! (dw/vertical-order-selected :up)) - do-bring-to-front #(st/emit! (dw/vertical-order-selected :top)) - do-send-backward #(st/emit! (dw/vertical-order-selected :down)) - do-send-to-back #(st/emit! (dw/vertical-order-selected :bottom)) + [{:keys [shapes]}] + (let [do-bring-forward (mf/use-fn #(st/emit! (dw/vertical-order-selected :up))) + do-bring-to-front (mf/use-fn #(st/emit! (dw/vertical-order-selected :top))) + do-send-backward (mf/use-fn #(st/emit! (dw/vertical-order-selected :down))) + do-send-to-back (mf/use-fn #(st/emit! (dw/vertical-order-selected :bottom))) + select-shapes (fn [id] #(st/emit! (dws/select-shape id))) on-pointer-enter (fn [id] #(st/emit! (dw/highlight-shape id))) on-pointer-leave (fn [id] #(st/emit! (dw/dehighlight-shape id))) - on-unmount (fn [id] #(st/emit! (dw/dehighlight-shape id)))] + on-unmount (fn [id] #(st/emit! (dw/dehighlight-shape id))) + + ;; NOTE: we use deref instead of mf/deref on objects because + ;; we really don't want rerender on object changes + hover-ids (deref refs/current-hover-ids) + objects (deref refs/workspace-page-objects) + hover-objs (into [] (keep (d/getf objects)) hover-ids)] + [:* (when (> (count hover-objs) 1) [:& menu-entry {:title (tr "workspace.shape.menu.select-layer")} (for [object hover-objs] [:& menu-entry {:title (:name object) + :key (dm/str (:id object)) :selected? (some #(= object %) shapes) :on-click (select-shapes (:id object)) :on-pointer-enter (on-pointer-enter (:id object)) @@ -448,14 +458,11 @@ :on-click do-delete}])) (mf/defc shape-context-menu + {::mf/wrap [mf/memo]} [{:keys [mdata] :as props}] (let [{:keys [disable-booleans? disable-flatten?]} mdata shapes (mf/deref refs/selected-objects) - hover-ids (mf/deref refs/current-hover-ids) - hover-objs (mf/deref (refs/objects-by-id hover-ids)) - props #js {:shapes shapes - :hover-objs hover-objs :disable-booleans? disable-booleans? :disable-flatten? disable-flatten?}] (when-not (empty? shapes) From 10f8d1365c66c9e5f884904f332b3c2c03c6e2ee Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 09:38:14 +0200 Subject: [PATCH 10/12] :paperclip: Add todo about equality checks on refs ns --- frontend/src/app/main/refs.cljs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index ce4f0b8d5..46302a4a1 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -251,6 +251,8 @@ [page-id] (l/derived #(wsh/lookup-page-objects % page-id) st/state =)) +;; TODO: Looks like using the `=` comparator can be pretty expensive +;; on large pages, we are using this for some reason? (def workspace-page-objects (l/derived wsh/lookup-page-objects st/state =)) From b3415d0d521ffd9d40804dd391782e0a1c75061f Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 09:51:11 +0200 Subject: [PATCH 11/12] :paperclip: Update changelog --- CHANGES.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 2db27cda3..9bb6075c8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,12 +5,15 @@ ### :boom: Breaking changes & Deprecations ### :sparkles: New features -- Add some cosmetic changes in viewer mode [Taiga #3688](https://tree.taiga.io/project/penpot/us/3688) +- Add cosmetic changes in viewer mode [Taiga #3688](https://tree.taiga.io/project/penpot/us/3688) +- Outline highlights on layer hovering [Taiga #2645](https://tree.taiga.io/project/penpot/us/2645) by @andrewzhurov ### :bug: Bugs fixed ### :arrow_up: Deps updates ### :heart: Community contributions by (Thank you!) +- To @andrewzhurov for many code contributions on this release. + ## 1.15.0-beta From 7a3f1a36e9c72ff2b11c0527f9e03cd49a46dc0f Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 28 Jul 2022 09:53:51 +0200 Subject: [PATCH 12/12] :paperclip: Fix linter issues --- frontend/src/app/main/data/workspace.cljs | 2 +- frontend/src/app/main/data/workspace/highlight.cljs | 3 ++- frontend/src/app/main/ui/workspace/sidebar/layers.cljs | 4 ++-- frontend/src/app/main/ui/workspace/viewport/outline.cljs | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 451180143..62dc383ad 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -35,6 +35,7 @@ [app.main.data.workspace.fix-bool-contents :as fbc] [app.main.data.workspace.groups :as dwg] [app.main.data.workspace.guides :as dwgu] + [app.main.data.workspace.highlight :as dwh] [app.main.data.workspace.interactions :as dwi] [app.main.data.workspace.layers :as dwly] [app.main.data.workspace.layout :as layout] @@ -45,7 +46,6 @@ [app.main.data.workspace.path.shapes-to-path :as dwps] [app.main.data.workspace.persistence :as dwp] [app.main.data.workspace.selection :as dws] - [app.main.data.workspace.highlight :as dwh] [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.state-helpers :as wsh] diff --git a/frontend/src/app/main/data/workspace/highlight.cljs b/frontend/src/app/main/data/workspace/highlight.cljs index 3968295c7..f34a1b323 100644 --- a/frontend/src/app/main/data/workspace/highlight.cljs +++ b/frontend/src/app/main/data/workspace/highlight.cljs @@ -7,6 +7,7 @@ (ns app.main.data.workspace.highlight (:require [app.common.spec :as us] + [clojure.set :as set] [potok.core :as ptk])) ;; --- Manage shape's highlight status @@ -17,7 +18,7 @@ (ptk/reify ::highlight-shape ptk/UpdateEvent (update [_ state] - (update-in state [:workspace-local :highlighted] clojure.set/union #{id})))) + (update-in state [:workspace-local :highlighted] set/union #{id})))) (defn dehighlight-shape [id] diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index a780c566a..dc82c6deb 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -151,13 +151,13 @@ on-pointer-enter (mf/use-fn (mf/deps id) - (fn [event] + (fn [_event] (st/emit! (dw/highlight-shape id)))) on-pointer-leave (mf/use-fn (mf/deps id) - (fn [event] + (fn [_event] (st/emit! (dw/dehighlight-shape id)))) on-context-menu diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index 17bd7734b..87498e154 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -6,9 +6,9 @@ (ns app.main.ui.workspace.viewport.outline (:require + [app.common.data :as d] [app.common.exceptions :as ex] [app.common.geom.shapes :as gsh] - [app.common.data :as d] [app.util.object :as obj] [app.util.path.format :as upf] [clojure.set :as set]