diff --git a/CHANGES.md b/CHANGES.md index a3a4177a8a..efe24c3016 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -61,6 +61,14 @@ - Update Work-Sans font [#744](https://github.com/penpot/penpot/issues/744) - Fix issue with recent files not showing [Taiga #1493](https://tree.taiga.io/project/penpot/issue/1493) - Fix issue when promoting to owner [Taiga #1494](https://tree.taiga.io/project/penpot/issue/1494) +- Fix cannot click on blocked elements in viewer [Taiga #1430](https://tree.taiga.io/project/penpot/issue/1430) +- Fix SVG not showing properties at code [Taiga #1437](https://tree.taiga.io/project/penpot/issue/1437) +- Fix shadows when exporting groups [Taiga #1495](https://tree.taiga.io/project/penpot/issue/1495) +- Fix drag-select when renaming layer text [Taiga #1307](https://tree.taiga.io/project/penpot/issue/1307) +- Fix layout problem for editable selects [Taiga #1488](https://tree.taiga.io/project/penpot/issue/1488) +- Fix artboard title wasn't move when resizing [Taiga #1479](https://tree.taiga.io/project/penpot/issue/1479) +- Fix titles in viewer thumbnails too long [Taiga #1474](https://tree.taiga.io/project/penpot/issue/1474) +- Fix when right click on a selected text shows artboard contextual menu [Taiga #1226](https://tree.taiga.io/project/penpot/issue/1226) >>>>>>> origin/staging diff --git a/frontend/resources/styles/main/partials/sidebar-element-options.scss b/frontend/resources/styles/main/partials/sidebar-element-options.scss index 80dddb37a8..e0770692dd 100644 --- a/frontend/resources/styles/main/partials/sidebar-element-options.scss +++ b/frontend/resources/styles/main/partials/sidebar-element-options.scss @@ -420,6 +420,10 @@ position: absolute; right: 0; padding-right: 4px; + height: 100%; + display: flex; + align-items: center; + } &.input-option { @@ -438,6 +442,11 @@ } } + .custom-select-dropdown { + top: unset; + margin-bottom: 0; + } + &:hover { border: 1px solid $color-gray-40; } @@ -449,10 +458,10 @@ height: 2rem; } .editable-select svg { - fill: $color-gray-40; + fill: $color-gray-40; } .dropdown-button { - top: 4px; + top: 4px; } .editable-select.input-option .input-text { padding: 0; @@ -778,8 +787,8 @@ margin-left: 0.5rem; & .custom-select-dropdown { - width: 56px; - min-width: 56px; + width: 5rem; + min-width: 5rem; max-height: 10rem; } } @@ -1004,7 +1013,6 @@ cursor: pointer; max-height: 16rem; min-width: 6rem; - margin-top: 25px; left: initial; top: 0; } diff --git a/frontend/resources/styles/main/partials/viewer-thumbnails.scss b/frontend/resources/styles/main/partials/viewer-thumbnails.scss index bd92f3de9b..d630c8bfc3 100644 --- a/frontend/resources/styles/main/partials/viewer-thumbnails.scss +++ b/frontend/resources/styles/main/partials/viewer-thumbnails.scss @@ -165,6 +165,10 @@ .thumbnail-info { padding: 0.5rem 0; + width: 120px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; span { font-size: $fs13; diff --git a/frontend/src/app/main/data/users.cljs b/frontend/src/app/main/data/users.cljs index 178d4ff830..a586b81266 100644 --- a/frontend/src/app/main/data/users.cljs +++ b/frontend/src/app/main/data/users.cljs @@ -77,11 +77,7 @@ ptk/WatchEvent (watch [_ state s] (->> (rp/query! :profile) - (rx/map profile-fetched) - (rx/catch (fn [error] - (if (= (:type error) :not-found) - (rx/of (rt/nav :auth-login)) - (rx/empty)))))))) + (rx/map profile-fetched))))) ;; --- Update Profile diff --git a/frontend/src/app/main/ui/components/editable_select.cljs b/frontend/src/app/main/ui/components/editable_select.cljs index 4d9ffdb806..da11ea4fa5 100644 --- a/frontend/src/app/main/ui/components/editable_select.cljs +++ b/frontend/src/app/main/ui/components/editable_select.cljs @@ -13,16 +13,19 @@ [app.common.uuid :as uuid] [app.common.data :as d] [app.util.dom :as dom] + [app.util.timers :as timers] [app.main.ui.icons :as i] [app.main.ui.components.dropdown :refer [dropdown]])) (mf/defc editable-select [{:keys [value type options class on-change placeholder]}] (let [state (mf/use-state {:id (uuid/next) :is-open? false - :current-value value}) + :current-value value + :top nil + :left nil + :bottom nil}) open-dropdown #(swap! state assoc :is-open? true) close-dropdown #(swap! state assoc :is-open? false) - select-item (fn [value] (fn [event] (swap! state assoc :current-value value) @@ -38,7 +41,23 @@ (let [value (-> event dom/get-target dom/get-value) value (or (d/parse-integer value) value)] (swap! state assoc :current-value value) - (when on-change (on-change value))))] + (when on-change (on-change value)))) + + on-node-load + (fn [node] + ;; There is a problem when changing the state in this callback that + ;; produces the dropdown to close in the same event + (timers/schedule + #(when-let [bounds (when node (dom/get-bounding-rect node))] + (let [{window-height :height} (dom/get-window-size) + {:keys [left top height]} bounds + bottom (when (< (- window-height top) 300) (- window-height top)) + top (when (>= (- window-height top) 300) (+ top height))] + (swap! state + assoc + :left left + :top top + :bottom bottom)))))] (mf/use-effect (mf/deps value) @@ -49,7 +68,8 @@ #(reset! state {:is-open? false :current-value value})) - [:div.editable-select {:class class} + [:div.editable-select {:class class + :ref on-node-load} [:input.input-text {:value (or (-> @state :current-value value->label) "") :on-change handle-change-input :placeholder placeholder @@ -58,7 +78,10 @@ [:& dropdown {:show (get @state :is-open? false) :on-close close-dropdown} - [:ul.custom-select-dropdown + [:ul.custom-select-dropdown {:style {:position "fixed" + :top (:top @state) + :left (:left @state) + :bottom (:bottom @state)}} (for [[index item] (map-indexed vector options)] (cond (= :separator item) [:hr {:key (str (:id @state) "-" index)}] diff --git a/frontend/src/app/main/ui/dashboard/team_form.cljs b/frontend/src/app/main/ui/dashboard/team_form.cljs index d743f76dca..f27c93642d 100644 --- a/frontend/src/app/main/ui/dashboard/team_form.cljs +++ b/frontend/src/app/main/ui/dashboard/team_form.cljs @@ -5,27 +5,22 @@ ;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; defined by the Mozilla Public License, v. 2.0. ;; -;; Copyright (c) 2020 UXBOX Labs SL +;; Copyright (c) UXBOX Labs SL (ns app.main.ui.dashboard.team-form (:require [app.common.data :as d] [app.common.spec :as us] - [app.config :as cfg] - [app.main.data.auth :as da] [app.main.data.dashboard :as dd] [app.main.data.messages :as dm] [app.main.data.modal :as modal] - [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.main.ui.icons :as i] [app.util.dom :as dom] - [app.util.i18n :as i18n :refer [t tr]] - [app.util.keyboard :as kbd] + [app.util.i18n :as i18n :refer [tr]] [app.util.object :as obj] [app.util.router :as rt] - [app.util.time :as dt] [beicon.core :as rx] [cljs.spec.alpha :as s] [cuerdas.core :as str] @@ -52,10 +47,8 @@ [form response] (let [id (get-in @form [:clean-data :id])] (if id - (st/emit! (dm/error "Error on updating team.")) - (st/emit! (dm/error "Error on creating team."))))) - -;; TODO: check global error handler + (rx/of (dm/error "Error on updating team.")) + (rx/of (dm/error "Error on creating team."))))) (defn- on-create-submit [form] @@ -76,8 +69,7 @@ {::mf/register modal/components ::mf/register-as :team-form} [{:keys [team] :as props}] - (let [locale (mf/deref i18n/locale) - form (fm/use-form :spec ::team-form + (let [form (fm/use-form :spec ::team-form :initial (or team {})) on-submit @@ -89,19 +81,20 @@ [:div.modal-overlay [:div.modal-container.team-form-modal - [:& fm/form {:form form - :on-submit on-submit} + [:& fm/form {:form form :on-submit on-submit} [:div.modal-header [:div.modal-header-title (if team [:h2 (tr "labels.rename-team")] [:h2 (tr "labels.create-team")])] + [:div.modal-close-button {:on-click (st/emitf (modal/hide))} i/close]] [:div.modal-content.generic-form [:& fm/input {:type "text" + :auto-focus true :form form :name :name :label "Enter new team name:"}]] diff --git a/frontend/src/app/main/ui/handoff/render.cljs b/frontend/src/app/main/ui/handoff/render.cljs index a039dc3366..bcc0574b69 100644 --- a/frontend/src/app/main/ui/handoff/render.cljs +++ b/frontend/src/app/main/ui/handoff/render.cljs @@ -63,10 +63,10 @@ (let [shape (unchecked-get props "shape") childs (unchecked-get props "childs") frame (unchecked-get props "frame") - svg-element? (and (= :svg-raw (:type shape)) - (not= :svg (get-in shape [:content :tag])))] + render-wrapper? (or (not= :svg-raw (:type shape)) + (svg-raw/graphic-element? (get-in shape [:content :tag])))] - (if-not svg-element? + (if render-wrapper? [:> shape-container {:shape shape :on-mouse-enter (handle-hover-shape shape true) :on-mouse-leave (handle-hover-shape shape false) diff --git a/frontend/src/app/main/ui/hooks.cljs b/frontend/src/app/main/ui/hooks.cljs index 81c7f6fa4f..81fcba93e0 100644 --- a/frontend/src/app/main/ui/hooks.cljs +++ b/frontend/src/app/main/ui/hooks.cljs @@ -14,6 +14,7 @@ [app.common.spec :as us] [app.main.data.shortcuts :refer [bind-shortcuts]] [app.util.dom :as dom] + [app.util.object :as obj] [app.util.dom.dnd :as dnd] [app.util.logging :as log] [app.util.timers :as ts] @@ -97,7 +98,7 @@ ;; things go weird. (defn use-sortable - [& {:keys [data-type data on-drop on-drag on-hold detect-center?] :as opts}] + [& {:keys [data-type data on-drop on-drag on-hold disabled detect-center?] :as opts}] (let [ref (mf/use-ref) state (mf/use-state {:over nil :timer nil @@ -125,13 +126,16 @@ on-drag-start (fn [event] - (dom/stop-propagation event) - ;; (dnd/trace event data "drag-start") - (dnd/set-data! event data-type data) - (dnd/set-drag-image! event (invisible-image)) - (dnd/set-allowed-effect! event "move") - (when (fn? on-drag) - (on-drag data))) + (if disabled + (dom/prevent-default event) + (let [target (dom/get-target event)] + (dom/stop-propagation event) + ;; (dnd/trace event data "drag-start") + (dnd/set-data! event data-type data) + (dnd/set-drag-image! event (invisible-image)) + (dnd/set-allowed-effect! event "move") + (when (fn? on-drag) + (on-drag data))))) on-drag-enter (fn [event] diff --git a/frontend/src/app/main/ui/render.cljs b/frontend/src/app/main/ui/render.cljs index e02da77e20..79c8d83a48 100644 --- a/frontend/src/app/main/ui/render.cljs +++ b/frontend/src/app/main/ui/render.cljs @@ -9,18 +9,21 @@ (ns app.main.ui.render (:require - [cljs.spec.alpha :as s] - [beicon.core :as rx] - [rumext.alpha :as mf] - [app.common.uuid :as uuid] - [app.common.pages :as cp] - [app.common.math :as mth] - [app.common.geom.shapes :as geom] - [app.common.geom.point :as gpt] [app.common.geom.matrix :as gmt] - [app.main.ui.context :as muc] + [app.common.geom.point :as gpt] + [app.common.geom.shapes :as gsh] + [app.common.math :as mth] + [app.common.pages :as cp] + [app.common.uuid :as uuid] [app.main.exports :as exports] - [app.main.repo :as repo])) + [app.main.repo :as repo] + [app.main.ui.context :as muc] + [app.main.ui.shapes.filters :as filters] + [app.main.ui.shapes.shape :refer [shape-container]] + [beicon.core :as rx] + [cljs.spec.alpha :as s] + [cuerdas.core :as str] + [rumext.alpha :as mf])) (mf/defc object-svg {::mf/wrap [mf/memo]} @@ -37,18 +40,23 @@ mod-ids (cons frame-id (cp/get-children frame-id objects)) updt-fn #(-> %1 (assoc-in [%2 :modifiers :displacement] modifier) - (update %2 geom/transform-shape)) + (update %2 gsh/transform-shape)) objects (reduce updt-fn objects mod-ids) object (get objects object-id) - width (* (get-in object [:selrect :width]) zoom) - height (* (get-in object [:selrect :height]) zoom) - vbox (str (get-in object [:selrect :x]) " " - (get-in object [:selrect :y]) " " - (get-in object [:selrect :width]) " " - (get-in object [:selrect :height])) + {:keys [width height]} (gsh/points->selrect (:points object)) + + ;; We need to get the shadows/blurs paddings to create the viewbox properly + {:keys [x y width height]} (filters/get-filters-bounds object) + + x (* x zoom) + y (* y zoom) + width (* width zoom) + height (* height zoom) + + vbox (str/join " " [x y width height]) frame-wrapper (mf/use-memo @@ -76,7 +84,8 @@ :xmlns "http://www.w3.org/2000/svg"} (case (:type object) :frame [:& frame-wrapper {:shape object :view-box vbox}] - :group [:& group-wrapper {:shape object}] + :group [:> shape-container {:shape object} + [:& group-wrapper {:shape object}]] [:& shape-wrapper {:shape object}])]])) (defn- adapt-root-frame @@ -84,9 +93,9 @@ (if (uuid/zero? object-id) (let [object (get objects object-id) shapes (cp/select-toplevel-shapes objects {:include-frames? true}) - srect (geom/selection-rect shapes) + srect (gsh/selection-rect shapes) object (merge object (select-keys srect [:x :y :width :height])) - object (geom/transform-shape object) + object (gsh/transform-shape object) object (assoc object :fill-color "#f0f0f0")] (assoc objects (:id object) object)) objects)) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index 463ece89d8..d7b7ed3647 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -122,40 +122,6 @@ :x2 (+ filter-x filter-width) :y2 (+ filter-y filter-height)})) -(defn get-filters-bounds - [shape filters blur-value] - - (let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag]))) - frame? (= :frame (:type shape)) - {:keys [x y width height]} (:selrect shape)] - (if svg-root? - ;; When is a raw-svg but not the root we use the whole svg as bound for the filter. Is the maximum - ;; we're allowed to display - {:x 0 :y 0 :width width :height height} - - ;; Otherwise we calculate the bound - (let [filter-bounds (->> filters - (filter #(= :drop-shadow (:type %))) - (map (partial filter-bounds shape) )) - ;; We add the selrect so the minimum size will be the selrect - filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect)) - x1 (apply min (map :x1 filter-bounds)) - y1 (apply min (map :y1 filter-bounds)) - x2 (apply max (map :x2 filter-bounds)) - y2 (apply max (map :y2 filter-bounds)) - - x1 (- x1 (* blur-value 2)) - x2 (+ x2 (* blur-value 2)) - y1 (- y1 (* blur-value 2)) - y2 (+ y2 (* blur-value 2))] - - ;; We should move the frame filter coordinates because they should be - ;; relative with the frame. By default they come as absolute - {:x (if frame? (- x1 x) x1) - :y (if frame? (- y1 y) y1) - :width (- x2 x1) - :height (- y2 y1)})))) - (defn blur-filters [type value] (->> [value] (remove :hidden) @@ -184,21 +150,64 @@ :image-fix [:> image-fix-filter props] :blend-filters [:> blend-filters props]))) +(defn shape->filters + [shape] + (d/concat + [] + [{:id "BackgroundImageFix" :type :image-fix}] + + ;; Background blur won't work in current SVG specification + ;; We can revisit this in the future + #_(->> shape :blur (blur-filters :background-blur)) + + (->> shape :shadow (shadow-filters :drop-shadow)) + [{:id "shape" :type :blend-filters}] + (->> shape :shadow (shadow-filters :inner-shadow)) + (->> shape :blur (blur-filters :layer-blur)))) + +(defn get-filters-bounds + ([shape] + (let [filters (shape->filters shape) + blur-value (or (-> shape :blur :value) 0)] + (get-filters-bounds shape filters blur-value))) + + ([shape filters blur-value] + + (let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag]))) + frame? (= :frame (:type shape)) + {:keys [x y width height]} (:selrect shape)] + (if svg-root? + ;; When is a raw-svg but not the root we use the whole svg as bound for the filter. Is the maximum + ;; we're allowed to display + {:x 0 :y 0 :width width :height height} + + ;; Otherwise we calculate the bound + (let [filter-bounds (->> filters + (filter #(= :drop-shadow (:type %))) + (map (partial filter-bounds shape))) + ;; We add the selrect so the minimum size will be the selrect + filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect)) + x1 (apply min (map :x1 filter-bounds)) + y1 (apply min (map :y1 filter-bounds)) + x2 (apply max (map :x2 filter-bounds)) + y2 (apply max (map :y2 filter-bounds)) + + x1 (- x1 (* blur-value 2)) + x2 (+ x2 (* blur-value 2)) + y1 (- y1 (* blur-value 2)) + y2 (+ y2 (* blur-value 2))] + + ;; We should move the frame filter coordinates because they should be + ;; relative with the frame. By default they come as absolute + {:x (if frame? (- x1 x) x1) + :y (if frame? (- y1 y) y1) + :width (- x2 x1) + :height (- y2 y1)}))))) + (mf/defc filters [{:keys [filter-id shape]}] - (let [filters (d/concat - [] - [{:id "BackgroundImageFix" :type :image-fix}] - - ;; Background blur won't work in current SVG specification - ;; We can revisit this in the future - #_(->> shape :blur (blur-filters :background-blur)) - - (->> shape :shadow (shadow-filters :drop-shadow)) - [{:id "shape" :type :blend-filters}] - (->> shape :shadow (shadow-filters :inner-shadow)) - (->> shape :blur (blur-filters :layer-blur))) + (let [filters (shape->filters shape) ;; Adds the previous filter as `filter-in` parameter filters (map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters))) diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index 3e18f6ea04..1b145d9c88 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -20,12 +20,13 @@ (mf/defc shape-container {::mf/wrap-props false} [props] - (let [shape (obj/get props "shape") - children (obj/get props "children") - render-id (mf/use-memo #(str (uuid/next))) - filter-id (str "filter_" render-id) - styles (cond-> (obj/new) - (:blocked shape) (obj/set! "pointerEvents" "none")) + (let [shape (obj/get props "shape") + children (obj/get props "children") + pointer-events (obj/get props "pointer-events") + render-id (mf/use-memo #(str (uuid/next))) + filter-id (str "filter_" render-id) + styles (-> (obj/new) + (obj/set! "pointerEvents" pointer-events)) {:keys [x y width height type]} shape frame? (= :frame type) diff --git a/frontend/src/app/main/ui/shapes/svg_raw.cljs b/frontend/src/app/main/ui/shapes/svg_raw.cljs index f660392474..401221ce2a 100644 --- a/frontend/src/app/main/ui/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/shapes/svg_raw.cljs @@ -21,7 +21,8 @@ [rumext.alpha :as mf])) ;; Graphic tags -(defonce graphic-element? #{:circle :ellipse :image :line :path :polygon :polyline :rect :text :use}) +(defonce graphic-element? + #{:svg :circle :ellipse :image :line :path :polygon :polyline :rect :symbol :text :textPath :use}) ;; Context to store a re-mapping of the ids (def svg-ids-ctx (mf/create-context nil)) diff --git a/frontend/src/app/main/ui/viewer/thumbnails.cljs b/frontend/src/app/main/ui/viewer/thumbnails.cljs index b2af4a5155..2c740911a2 100644 --- a/frontend/src/app/main/ui/viewer/thumbnails.cljs +++ b/frontend/src/app/main/ui/viewer/thumbnails.cljs @@ -91,7 +91,7 @@ {:class (classnames :selected selected?)} [:& exports/frame-svg {:frame frame :objects objects}]] [:div.thumbnail-info - [:span.name (:name frame)]]]) + [:span.name {:title (:name frame)} (:name frame)]]]) (mf/defc thumbnails-panel [{:keys [data index screen] :as props}] diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs index 0383ef1430..1996ed1402 100644 --- a/frontend/src/app/main/ui/workspace/shapes.cljs +++ b/frontend/src/app/main/ui/workspace/shapes.cljs @@ -76,7 +76,8 @@ shape (-> (geom/transform-shape shape) (geom/translate-to-frame frame)) opts #js {:shape shape - :frame frame} + :frame frame + :pointer-events (when (:blocked shape) "none")} svg-element? (and (= (:type shape) :svg-raw) (not= :svg (get-in shape [:content :tag])))] diff --git a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs index 57eefbb868..68ed9d629b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/svg_raw.cljs @@ -16,10 +16,6 @@ [app.common.geom.shapes :as gsh] [app.main.ui.context :as muc])) -;; This is a list of svg tags that can be grouped in shape-container -;; this allows them to have gradients, shadows and masks -(def svg-elements #{:svg :circle :ellipse :image :line :path :polygon :polyline :rect :symbol :text :textPath :use}) - (defn svg-raw-wrapper-factory [shape-wrapper] (let [svg-raw-shape (svg-raw/svg-raw-shape shape-wrapper)] @@ -43,21 +39,12 @@ def-ctx? (mf/use-ctx muc/def-ctx)] (cond - (and (contains? svg-elements tag) (not def-ctx?)) + (and (svg-raw/graphic-element? tag) (not def-ctx?)) [:> shape-container { :shape shape } [:& svg-raw-shape {:frame frame :shape shape - :childs childs}] - - [:rect.actions - {:x x - :y y - :transform transform - :width width - :height height - :fill "transparent" - :stroke "none"}]] + :childs childs}]] ;; We cannot wrap inside groups the shapes that go inside the defs tag ;; we use the context so we know when we should not render the container diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index ecd5f26ee9..b49e900877 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -54,31 +54,25 @@ (l/derived (l/in [:workspace-local :shape-for-rename]) st/state)) (mf/defc layer-name - [{:keys [shape] :as props}] + [{:keys [shape on-start-edit on-stop-edit] :as props}] (let [local (mf/use-state {}) shape-for-rename (mf/deref shape-for-rename-ref) name-ref (mf/use-ref) - set-draggable (fn [value] - (let [parent (.. (mf/ref-val name-ref) - -parentNode - -parentNode)] - (set! (.-draggable parent) value))) - start-edit (fn [] - (set-draggable false) + (on-start-edit) (swap! local assoc :edition true)) accept-edit (fn [] (let [name-input (mf/ref-val name-ref) name (dom/get-value name-input)] - (set-draggable true) + (on-stop-edit) (swap! local assoc :edition false) (st/emit! (dw/end-rename-shape) (dw/update-shape (:id shape) {:name name})))) cancel-edit (fn [] - (set-draggable true) + (on-stop-edit) (swap! local assoc :edition false) (st/emit! (dw/end-rename-shape))) @@ -124,6 +118,8 @@ selected? (contains? selected id) container? (or (= (:type item) :frame) (= (:type item) :group)) + disable-drag (mf/use-state false) + expanded-iref (mf/use-memo (mf/deps id) (make-collapsed-iref id)) @@ -203,6 +199,7 @@ :on-drop on-drop :on-drag on-drag :on-hold on-hold + :disabled @disable-drag :detect-center? container? :data {:id (:id item) :index index @@ -229,7 +226,9 @@ :on-click select-shape :on-double-click #(dom/stop-propagation %)} [:& element-icon {:shape item}] - [:& layer-name {:shape item}] + [:& layer-name {:shape item + :on-start-edit #(reset! disable-drag true) + :on-stop-edit #(reset! disable-drag false)}] [:div.element-actions [:div.toggle-element {:class (when (:hidden item) "selected") diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 19fe7b742a..f68382016f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -34,46 +34,47 @@ (mf/use-callback (mf/deps id blocked hidden type drawing-tool text-editing? edition selected) (fn [bevent] - (dom/stop-propagation bevent) + (when (dom/class? (dom/get-target bevent) "viewport-controls") + (dom/stop-propagation bevent) - (let [event (.-nativeEvent bevent) - ctrl? (kbd/ctrl? event) - shift? (kbd/shift? event) - alt? (kbd/alt? event) + (let [event (.-nativeEvent bevent) + ctrl? (kbd/ctrl? event) + shift? (kbd/shift? event) + alt? (kbd/alt? event) - left-click? (= 1 (.-which event)) - middle-click? (= 2 (.-which event)) - - frame? (= :frame type) - selected? (contains? selected id)] + left-click? (= 1 (.-which event)) + middle-click? (= 2 (.-which event)) - (when middle-click? - (dom/prevent-default bevent) - (st/emit! (dw/start-panning))) + frame? (= :frame type) + selected? (contains? selected id)] - (when left-click? - (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?)) + (when middle-click? + (dom/prevent-default bevent) + (st/emit! (dw/start-panning))) - (when (and (not= edition id) text-editing?) - (st/emit! dw/clear-edition-mode)) + (when left-click? + (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?)) - (when (and (or (not edition) (not= edition id)) (not blocked) (not hidden)) - (cond - (and drawing-tool (not (#{:comments :path} drawing-tool))) - (st/emit! (dd/start-drawing drawing-tool)) + (when (and (not= edition id) text-editing?) + (st/emit! dw/clear-edition-mode)) - edit-path - ;; Handle node select-drawing. NOP at the moment - nil + (when (and (or (not edition) (not= edition id)) (not blocked) (not hidden)) + (cond + (and drawing-tool (not (#{:comments :path} drawing-tool))) + (st/emit! (dd/start-drawing drawing-tool)) - (or (not id) (and frame? (not selected?))) - (st/emit! (dw/handle-selection shift?)) + edit-path + ;; Handle node select-drawing. NOP at the moment + nil - :else - (st/emit! (when (or shift? (not selected?)) - (dw/select-shape id shift?)) - (when (not shift?) - (dw/start-move-selected)))))))))) + (or (not id) (and frame? (not selected?))) + (st/emit! (dw/handle-selection shift?)) + + :else + (st/emit! (when (or shift? (not selected?)) + (dw/select-shape id shift?)) + (when (not shift?) + (dw/start-move-selected))))))))))) (defn on-move-selected [hover hover-ids selected] @@ -122,17 +123,18 @@ (mf/use-callback (mf/deps @hover selected) (fn [event] - (let [ctrl? (kbd/ctrl? event) - shift? (kbd/shift? event) - alt? (kbd/alt? event) + (when (dom/class? (dom/get-target event) "viewport-controls") + (let [ctrl? (kbd/ctrl? event) + shift? (kbd/shift? event) + alt? (kbd/alt? event) - hovering? (some? @hover) - frame? (= :frame (:type @hover)) - selected? (contains? selected (:id @hover))] - (st/emit! (ms/->MouseEvent :click ctrl? shift? alt?)) + hovering? (some? @hover) + frame? (= :frame (:type @hover)) + selected? (contains? selected (:id @hover))] + (st/emit! (ms/->MouseEvent :click ctrl? shift? alt?)) - (when (and hovering? (not shift?) (not frame?) (not selected?)) - (st/emit! (dw/select-shape (:id @hover)))))))) + (when (and hovering? (not shift?) (not frame?) (not selected?)) + (st/emit! (dw/select-shape (:id @hover))))))))) (defn on-double-click [hover hover-ids drawing-path? objects] @@ -176,16 +178,17 @@ (mf/use-callback (mf/deps @hover) (fn [event] - (dom/prevent-default event) + (when (dom/class? (dom/get-target event) "viewport-controls") + (dom/prevent-default event) - (let [position (dom/get-client-position event)] - ;; Delayed callback because we need to wait to the previous context menu to be closed - (timers/schedule - #(st/emit! - (if (some? @hover) - (dw/show-shape-context-menu {:position position - :shape @hover}) - (dw/show-context-menu {:position position})))))))) + (let [position (dom/get-client-position event)] + ;; Delayed callback because we need to wait to the previous context menu to be closed + (timers/schedule + #(st/emit! + (if (some? @hover) + (dw/show-shape-context-menu {:position position + :shape @hover}) + (dw/show-context-menu {:position position}))))))))) (defn on-mouse-up [disable-paste] diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index c60d02293a..64ea034886 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -87,7 +87,7 @@ (mf/defc frame-title [{:keys [frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}] - (let [{:keys [width x y]} frame + (let [{:keys [width x y]} (gsh/transform-shape frame) label-pos (gpt/point x (- y (/ 10 zoom))) on-mouse-down