Merge branch 'staging' into develop

This commit is contained in:
Andrey Antukh 2021-03-30 08:49:32 +02:00
commit 9950f464ce
18 changed files with 238 additions and 192 deletions

View file

@ -61,6 +61,14 @@
- Update Work-Sans font [#744](https://github.com/penpot/penpot/issues/744) - 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 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 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 >>>>>>> origin/staging

View file

@ -420,6 +420,10 @@
position: absolute; position: absolute;
right: 0; right: 0;
padding-right: 4px; padding-right: 4px;
height: 100%;
display: flex;
align-items: center;
} }
&.input-option { &.input-option {
@ -438,6 +442,11 @@
} }
} }
.custom-select-dropdown {
top: unset;
margin-bottom: 0;
}
&:hover { &:hover {
border: 1px solid $color-gray-40; border: 1px solid $color-gray-40;
} }
@ -449,10 +458,10 @@
height: 2rem; height: 2rem;
} }
.editable-select svg { .editable-select svg {
fill: $color-gray-40; fill: $color-gray-40;
} }
.dropdown-button { .dropdown-button {
top: 4px; top: 4px;
} }
.editable-select.input-option .input-text { .editable-select.input-option .input-text {
padding: 0; padding: 0;
@ -778,8 +787,8 @@
margin-left: 0.5rem; margin-left: 0.5rem;
& .custom-select-dropdown { & .custom-select-dropdown {
width: 56px; width: 5rem;
min-width: 56px; min-width: 5rem;
max-height: 10rem; max-height: 10rem;
} }
} }
@ -1004,7 +1013,6 @@
cursor: pointer; cursor: pointer;
max-height: 16rem; max-height: 16rem;
min-width: 6rem; min-width: 6rem;
margin-top: 25px;
left: initial; left: initial;
top: 0; top: 0;
} }

View file

@ -165,6 +165,10 @@
.thumbnail-info { .thumbnail-info {
padding: 0.5rem 0; padding: 0.5rem 0;
width: 120px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
span { span {
font-size: $fs13; font-size: $fs13;

View file

@ -77,11 +77,7 @@
ptk/WatchEvent ptk/WatchEvent
(watch [_ state s] (watch [_ state s]
(->> (rp/query! :profile) (->> (rp/query! :profile)
(rx/map profile-fetched) (rx/map profile-fetched)))))
(rx/catch (fn [error]
(if (= (:type error) :not-found)
(rx/of (rt/nav :auth-login))
(rx/empty))))))))
;; --- Update Profile ;; --- Update Profile

View file

@ -13,16 +13,19 @@
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.common.data :as d] [app.common.data :as d]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.timers :as timers]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.main.ui.components.dropdown :refer [dropdown]])) [app.main.ui.components.dropdown :refer [dropdown]]))
(mf/defc editable-select [{:keys [value type options class on-change placeholder]}] (mf/defc editable-select [{:keys [value type options class on-change placeholder]}]
(let [state (mf/use-state {:id (uuid/next) (let [state (mf/use-state {:id (uuid/next)
:is-open? false :is-open? false
:current-value value}) :current-value value
:top nil
:left nil
:bottom nil})
open-dropdown #(swap! state assoc :is-open? true) open-dropdown #(swap! state assoc :is-open? true)
close-dropdown #(swap! state assoc :is-open? false) close-dropdown #(swap! state assoc :is-open? false)
select-item (fn [value] select-item (fn [value]
(fn [event] (fn [event]
(swap! state assoc :current-value value) (swap! state assoc :current-value value)
@ -38,7 +41,23 @@
(let [value (-> event dom/get-target dom/get-value) (let [value (-> event dom/get-target dom/get-value)
value (or (d/parse-integer value) value)] value (or (d/parse-integer value) value)]
(swap! state assoc :current-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/use-effect
(mf/deps value) (mf/deps value)
@ -49,7 +68,8 @@
#(reset! state {:is-open? false #(reset! state {:is-open? false
:current-value value})) :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) "") [:input.input-text {:value (or (-> @state :current-value value->label) "")
:on-change handle-change-input :on-change handle-change-input
:placeholder placeholder :placeholder placeholder
@ -58,7 +78,10 @@
[:& dropdown {:show (get @state :is-open? false) [:& dropdown {:show (get @state :is-open? false)
:on-close close-dropdown} :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)] (for [[index item] (map-indexed vector options)]
(cond (cond
(= :separator item) [:hr {:key (str (:id @state) "-" index)}] (= :separator item) [:hr {:key (str (:id @state) "-" index)}]

View file

@ -5,27 +5,22 @@
;; This Source Code Form is "Incompatible With Secondary Licenses", as ;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0. ;; 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 (ns app.main.ui.dashboard.team-form
(:require (:require
[app.common.data :as d] [app.common.data :as d]
[app.common.spec :as us] [app.common.spec :as us]
[app.config :as cfg]
[app.main.data.auth :as da]
[app.main.data.dashboard :as dd] [app.main.data.dashboard :as dd]
[app.main.data.messages :as dm] [app.main.data.messages :as dm]
[app.main.data.modal :as modal] [app.main.data.modal :as modal]
[app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.forms :as fm] [app.main.ui.components.forms :as fm]
[app.main.ui.icons :as i] [app.main.ui.icons :as i]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.i18n :as i18n :refer [t tr]] [app.util.i18n :as i18n :refer [tr]]
[app.util.keyboard :as kbd]
[app.util.object :as obj] [app.util.object :as obj]
[app.util.router :as rt] [app.util.router :as rt]
[app.util.time :as dt]
[beicon.core :as rx] [beicon.core :as rx]
[cljs.spec.alpha :as s] [cljs.spec.alpha :as s]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -52,10 +47,8 @@
[form response] [form response]
(let [id (get-in @form [:clean-data :id])] (let [id (get-in @form [:clean-data :id])]
(if id (if id
(st/emit! (dm/error "Error on updating team.")) (rx/of (dm/error "Error on updating team."))
(st/emit! (dm/error "Error on creating team."))))) (rx/of (dm/error "Error on creating team.")))))
;; TODO: check global error handler
(defn- on-create-submit (defn- on-create-submit
[form] [form]
@ -76,8 +69,7 @@
{::mf/register modal/components {::mf/register modal/components
::mf/register-as :team-form} ::mf/register-as :team-form}
[{:keys [team] :as props}] [{:keys [team] :as props}]
(let [locale (mf/deref i18n/locale) (let [form (fm/use-form :spec ::team-form
form (fm/use-form :spec ::team-form
:initial (or team {})) :initial (or team {}))
on-submit on-submit
@ -89,19 +81,20 @@
[:div.modal-overlay [:div.modal-overlay
[:div.modal-container.team-form-modal [:div.modal-container.team-form-modal
[:& fm/form {:form form [:& fm/form {:form form :on-submit on-submit}
:on-submit on-submit}
[:div.modal-header [:div.modal-header
[:div.modal-header-title [:div.modal-header-title
(if team (if team
[:h2 (tr "labels.rename-team")] [:h2 (tr "labels.rename-team")]
[:h2 (tr "labels.create-team")])] [:h2 (tr "labels.create-team")])]
[:div.modal-close-button [:div.modal-close-button
{:on-click (st/emitf (modal/hide))} i/close]] {:on-click (st/emitf (modal/hide))} i/close]]
[:div.modal-content.generic-form [:div.modal-content.generic-form
[:& fm/input {:type "text" [:& fm/input {:type "text"
:auto-focus true
:form form :form form
:name :name :name :name
:label "Enter new team name:"}]] :label "Enter new team name:"}]]

View file

@ -63,10 +63,10 @@
(let [shape (unchecked-get props "shape") (let [shape (unchecked-get props "shape")
childs (unchecked-get props "childs") childs (unchecked-get props "childs")
frame (unchecked-get props "frame") frame (unchecked-get props "frame")
svg-element? (and (= :svg-raw (:type shape)) render-wrapper? (or (not= :svg-raw (:type shape))
(not= :svg (get-in shape [:content :tag])))] (svg-raw/graphic-element? (get-in shape [:content :tag])))]
(if-not svg-element? (if render-wrapper?
[:> shape-container {:shape shape [:> shape-container {:shape shape
:on-mouse-enter (handle-hover-shape shape true) :on-mouse-enter (handle-hover-shape shape true)
:on-mouse-leave (handle-hover-shape shape false) :on-mouse-leave (handle-hover-shape shape false)

View file

@ -14,6 +14,7 @@
[app.common.spec :as us] [app.common.spec :as us]
[app.main.data.shortcuts :refer [bind-shortcuts]] [app.main.data.shortcuts :refer [bind-shortcuts]]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.object :as obj]
[app.util.dom.dnd :as dnd] [app.util.dom.dnd :as dnd]
[app.util.logging :as log] [app.util.logging :as log]
[app.util.timers :as ts] [app.util.timers :as ts]
@ -97,7 +98,7 @@
;; things go weird. ;; things go weird.
(defn use-sortable (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) (let [ref (mf/use-ref)
state (mf/use-state {:over nil state (mf/use-state {:over nil
:timer nil :timer nil
@ -125,13 +126,16 @@
on-drag-start on-drag-start
(fn [event] (fn [event]
(dom/stop-propagation event) (if disabled
;; (dnd/trace event data "drag-start") (dom/prevent-default event)
(dnd/set-data! event data-type data) (let [target (dom/get-target event)]
(dnd/set-drag-image! event (invisible-image)) (dom/stop-propagation event)
(dnd/set-allowed-effect! event "move") ;; (dnd/trace event data "drag-start")
(when (fn? on-drag) (dnd/set-data! event data-type data)
(on-drag data))) (dnd/set-drag-image! event (invisible-image))
(dnd/set-allowed-effect! event "move")
(when (fn? on-drag)
(on-drag data)))))
on-drag-enter on-drag-enter
(fn [event] (fn [event]

View file

@ -9,18 +9,21 @@
(ns app.main.ui.render (ns app.main.ui.render
(:require (: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.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.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/defc object-svg
{::mf/wrap [mf/memo]} {::mf/wrap [mf/memo]}
@ -37,18 +40,23 @@
mod-ids (cons frame-id (cp/get-children frame-id objects)) mod-ids (cons frame-id (cp/get-children frame-id objects))
updt-fn #(-> %1 updt-fn #(-> %1
(assoc-in [%2 :modifiers :displacement] modifier) (assoc-in [%2 :modifiers :displacement] modifier)
(update %2 geom/transform-shape)) (update %2 gsh/transform-shape))
objects (reduce updt-fn objects mod-ids) objects (reduce updt-fn objects mod-ids)
object (get objects object-id) 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]) " " {:keys [width height]} (gsh/points->selrect (:points object))
(get-in object [:selrect :y]) " "
(get-in object [:selrect :width]) " " ;; We need to get the shadows/blurs paddings to create the viewbox properly
(get-in object [:selrect :height])) {: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 frame-wrapper
(mf/use-memo (mf/use-memo
@ -76,7 +84,8 @@
:xmlns "http://www.w3.org/2000/svg"} :xmlns "http://www.w3.org/2000/svg"}
(case (:type object) (case (:type object)
:frame [:& frame-wrapper {:shape object :view-box vbox}] :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}])]])) [:& shape-wrapper {:shape object}])]]))
(defn- adapt-root-frame (defn- adapt-root-frame
@ -84,9 +93,9 @@
(if (uuid/zero? object-id) (if (uuid/zero? object-id)
(let [object (get objects object-id) (let [object (get objects object-id)
shapes (cp/select-toplevel-shapes objects {:include-frames? true}) 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 (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")] object (assoc object :fill-color "#f0f0f0")]
(assoc objects (:id object) object)) (assoc objects (:id object) object))
objects)) objects))

View file

@ -122,40 +122,6 @@
:x2 (+ filter-x filter-width) :x2 (+ filter-x filter-width)
:y2 (+ filter-y filter-height)})) :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] (defn blur-filters [type value]
(->> [value] (->> [value]
(remove :hidden) (remove :hidden)
@ -184,21 +150,64 @@
:image-fix [:> image-fix-filter props] :image-fix [:> image-fix-filter props]
:blend-filters [:> blend-filters 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 (mf/defc filters
[{:keys [filter-id shape]}] [{:keys [filter-id shape]}]
(let [filters (d/concat (let [filters (shape->filters shape)
[]
[{: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)))
;; Adds the previous filter as `filter-in` parameter ;; Adds the previous filter as `filter-in` parameter
filters (map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters))) filters (map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters)))

View file

@ -20,12 +20,13 @@
(mf/defc shape-container (mf/defc shape-container
{::mf/wrap-props false} {::mf/wrap-props false}
[props] [props]
(let [shape (obj/get props "shape") (let [shape (obj/get props "shape")
children (obj/get props "children") children (obj/get props "children")
render-id (mf/use-memo #(str (uuid/next))) pointer-events (obj/get props "pointer-events")
filter-id (str "filter_" render-id) render-id (mf/use-memo #(str (uuid/next)))
styles (cond-> (obj/new) filter-id (str "filter_" render-id)
(:blocked shape) (obj/set! "pointerEvents" "none")) styles (-> (obj/new)
(obj/set! "pointerEvents" pointer-events))
{:keys [x y width height type]} shape {:keys [x y width height type]} shape
frame? (= :frame type) frame? (= :frame type)

View file

@ -21,7 +21,8 @@
[rumext.alpha :as mf])) [rumext.alpha :as mf]))
;; Graphic tags ;; 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 ;; Context to store a re-mapping of the ids
(def svg-ids-ctx (mf/create-context nil)) (def svg-ids-ctx (mf/create-context nil))

View file

@ -91,7 +91,7 @@
{:class (classnames :selected selected?)} {:class (classnames :selected selected?)}
[:& exports/frame-svg {:frame frame :objects objects}]] [:& exports/frame-svg {:frame frame :objects objects}]]
[:div.thumbnail-info [:div.thumbnail-info
[:span.name (:name frame)]]]) [:span.name {:title (:name frame)} (:name frame)]]])
(mf/defc thumbnails-panel (mf/defc thumbnails-panel
[{:keys [data index screen] :as props}] [{:keys [data index screen] :as props}]

View file

@ -76,7 +76,8 @@
shape (-> (geom/transform-shape shape) shape (-> (geom/transform-shape shape)
(geom/translate-to-frame frame)) (geom/translate-to-frame frame))
opts #js {:shape shape opts #js {:shape shape
:frame frame} :frame frame
:pointer-events (when (:blocked shape) "none")}
svg-element? (and (= (:type shape) :svg-raw) svg-element? (and (= (:type shape) :svg-raw)
(not= :svg (get-in shape [:content :tag])))] (not= :svg (get-in shape [:content :tag])))]

View file

@ -16,10 +16,6 @@
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.main.ui.context :as muc])) [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 (defn svg-raw-wrapper-factory
[shape-wrapper] [shape-wrapper]
(let [svg-raw-shape (svg-raw/svg-raw-shape 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)] def-ctx? (mf/use-ctx muc/def-ctx)]
(cond (cond
(and (contains? svg-elements tag) (not def-ctx?)) (and (svg-raw/graphic-element? tag) (not def-ctx?))
[:> shape-container { :shape shape } [:> shape-container { :shape shape }
[:& svg-raw-shape [:& svg-raw-shape
{:frame frame {:frame frame
:shape shape :shape shape
:childs childs}] :childs childs}]]
[:rect.actions
{:x x
:y y
:transform transform
:width width
:height height
:fill "transparent"
:stroke "none"}]]
;; We cannot wrap inside groups the shapes that go inside the defs tag ;; 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 ;; we use the context so we know when we should not render the container

View file

@ -54,31 +54,25 @@
(l/derived (l/in [:workspace-local :shape-for-rename]) st/state)) (l/derived (l/in [:workspace-local :shape-for-rename]) st/state))
(mf/defc layer-name (mf/defc layer-name
[{:keys [shape] :as props}] [{:keys [shape on-start-edit on-stop-edit] :as props}]
(let [local (mf/use-state {}) (let [local (mf/use-state {})
shape-for-rename (mf/deref shape-for-rename-ref) shape-for-rename (mf/deref shape-for-rename-ref)
name-ref (mf/use-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 [] start-edit (fn []
(set-draggable false) (on-start-edit)
(swap! local assoc :edition true)) (swap! local assoc :edition true))
accept-edit (fn [] accept-edit (fn []
(let [name-input (mf/ref-val name-ref) (let [name-input (mf/ref-val name-ref)
name (dom/get-value name-input)] name (dom/get-value name-input)]
(set-draggable true) (on-stop-edit)
(swap! local assoc :edition false) (swap! local assoc :edition false)
(st/emit! (dw/end-rename-shape) (st/emit! (dw/end-rename-shape)
(dw/update-shape (:id shape) {:name name})))) (dw/update-shape (:id shape) {:name name}))))
cancel-edit (fn [] cancel-edit (fn []
(set-draggable true) (on-stop-edit)
(swap! local assoc :edition false) (swap! local assoc :edition false)
(st/emit! (dw/end-rename-shape))) (st/emit! (dw/end-rename-shape)))
@ -124,6 +118,8 @@
selected? (contains? selected id) selected? (contains? selected id)
container? (or (= (:type item) :frame) (= (:type item) :group)) container? (or (= (:type item) :frame) (= (:type item) :group))
disable-drag (mf/use-state false)
expanded-iref (mf/use-memo expanded-iref (mf/use-memo
(mf/deps id) (mf/deps id)
(make-collapsed-iref id)) (make-collapsed-iref id))
@ -203,6 +199,7 @@
:on-drop on-drop :on-drop on-drop
:on-drag on-drag :on-drag on-drag
:on-hold on-hold :on-hold on-hold
:disabled @disable-drag
:detect-center? container? :detect-center? container?
:data {:id (:id item) :data {:id (:id item)
:index index :index index
@ -229,7 +226,9 @@
:on-click select-shape :on-click select-shape
:on-double-click #(dom/stop-propagation %)} :on-double-click #(dom/stop-propagation %)}
[:& element-icon {:shape item}] [:& 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.element-actions
[:div.toggle-element {:class (when (:hidden item) "selected") [:div.toggle-element {:class (when (:hidden item) "selected")

View file

@ -34,46 +34,47 @@
(mf/use-callback (mf/use-callback
(mf/deps id blocked hidden type drawing-tool text-editing? edition selected) (mf/deps id blocked hidden type drawing-tool text-editing? edition selected)
(fn [bevent] (fn [bevent]
(dom/stop-propagation bevent) (when (dom/class? (dom/get-target bevent) "viewport-controls")
(dom/stop-propagation bevent)
(let [event (.-nativeEvent bevent) (let [event (.-nativeEvent bevent)
ctrl? (kbd/ctrl? event) ctrl? (kbd/ctrl? event)
shift? (kbd/shift? event) shift? (kbd/shift? event)
alt? (kbd/alt? event) alt? (kbd/alt? event)
left-click? (= 1 (.-which event)) left-click? (= 1 (.-which event))
middle-click? (= 2 (.-which event)) middle-click? (= 2 (.-which event))
frame? (= :frame type)
selected? (contains? selected id)]
(when middle-click? frame? (= :frame type)
(dom/prevent-default bevent) selected? (contains? selected id)]
(st/emit! (dw/start-panning)))
(when left-click? (when middle-click?
(st/emit! (ms/->MouseEvent :down ctrl? shift? alt?)) (dom/prevent-default bevent)
(st/emit! (dw/start-panning)))
(when (and (not= edition id) text-editing?) (when left-click?
(st/emit! dw/clear-edition-mode)) (st/emit! (ms/->MouseEvent :down ctrl? shift? alt?))
(when (and (or (not edition) (not= edition id)) (not blocked) (not hidden)) (when (and (not= edition id) text-editing?)
(cond (st/emit! dw/clear-edition-mode))
(and drawing-tool (not (#{:comments :path} drawing-tool)))
(st/emit! (dd/start-drawing drawing-tool))
edit-path (when (and (or (not edition) (not= edition id)) (not blocked) (not hidden))
;; Handle node select-drawing. NOP at the moment (cond
nil (and drawing-tool (not (#{:comments :path} drawing-tool)))
(st/emit! (dd/start-drawing drawing-tool))
(or (not id) (and frame? (not selected?))) edit-path
(st/emit! (dw/handle-selection shift?)) ;; Handle node select-drawing. NOP at the moment
nil
:else (or (not id) (and frame? (not selected?)))
(st/emit! (when (or shift? (not selected?)) (st/emit! (dw/handle-selection shift?))
(dw/select-shape id shift?))
(when (not shift?) :else
(dw/start-move-selected)))))))))) (st/emit! (when (or shift? (not selected?))
(dw/select-shape id shift?))
(when (not shift?)
(dw/start-move-selected)))))))))))
(defn on-move-selected (defn on-move-selected
[hover hover-ids selected] [hover hover-ids selected]
@ -122,17 +123,18 @@
(mf/use-callback (mf/use-callback
(mf/deps @hover selected) (mf/deps @hover selected)
(fn [event] (fn [event]
(let [ctrl? (kbd/ctrl? event) (when (dom/class? (dom/get-target event) "viewport-controls")
shift? (kbd/shift? event) (let [ctrl? (kbd/ctrl? event)
alt? (kbd/alt? event) shift? (kbd/shift? event)
alt? (kbd/alt? event)
hovering? (some? @hover) hovering? (some? @hover)
frame? (= :frame (:type @hover)) frame? (= :frame (:type @hover))
selected? (contains? selected (:id @hover))] selected? (contains? selected (:id @hover))]
(st/emit! (ms/->MouseEvent :click ctrl? shift? alt?)) (st/emit! (ms/->MouseEvent :click ctrl? shift? alt?))
(when (and hovering? (not shift?) (not frame?) (not selected?)) (when (and hovering? (not shift?) (not frame?) (not selected?))
(st/emit! (dw/select-shape (:id @hover)))))))) (st/emit! (dw/select-shape (:id @hover)))))))))
(defn on-double-click (defn on-double-click
[hover hover-ids drawing-path? objects] [hover hover-ids drawing-path? objects]
@ -176,16 +178,17 @@
(mf/use-callback (mf/use-callback
(mf/deps @hover) (mf/deps @hover)
(fn [event] (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)] (let [position (dom/get-client-position event)]
;; Delayed callback because we need to wait to the previous context menu to be closed ;; Delayed callback because we need to wait to the previous context menu to be closed
(timers/schedule (timers/schedule
#(st/emit! #(st/emit!
(if (some? @hover) (if (some? @hover)
(dw/show-shape-context-menu {:position position (dw/show-shape-context-menu {:position position
:shape @hover}) :shape @hover})
(dw/show-context-menu {:position position})))))))) (dw/show-context-menu {:position position})))))))))
(defn on-mouse-up (defn on-mouse-up
[disable-paste] [disable-paste]

View file

@ -87,7 +87,7 @@
(mf/defc frame-title (mf/defc frame-title
[{:keys [frame modifiers selected? zoom on-frame-enter on-frame-leave on-frame-select]}] [{: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))) label-pos (gpt/point x (- y (/ 10 zoom)))
on-mouse-down on-mouse-down