From 9dcad7ebef649e090f42450ac84d26b2fbc9c43b Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 19 Apr 2022 19:29:11 +0200 Subject: [PATCH 01/98] :bug: Round the size values on handoff to two decimals --- CHANGES.md | 1 + .../ui/viewer/handoff/attributes/layout.cljs | 21 ++++++++++--------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 3b0ed9a90..09a3ab9d2 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -50,6 +50,7 @@ - Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069) - Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221) +- Round the size values on handoff to two decimals [Taiga #3227](https://tree.taiga.io/project/penpot/issue/3227) - Fix paste shapes while editing text [Taiga #2396](https://tree.taiga.io/project/penpot/issue/2396) - Fix blend modes ignored in component updates [Taiga #2626](https://tree.taiga.io/project/penpot/issue/2626) - Fix internal error when hoverin over shape [Taiga #3237](https://tree.taiga.io/project/penpot/issue/3237) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs index fe7c7faa6..fe748716c 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -10,6 +10,7 @@ [app.main.ui.components.copy-button :refer [copy-button]] [app.util.code-gen :as cg] [app.util.i18n :refer [tr]] + [app.util.strings :as ust] [cuerdas.core :as str] [rumext.alpha :as mf])) @@ -38,46 +39,46 @@ [:* [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.width")] - [:div.attributes-value width "px"] + [:div.attributes-value (ust/format-precision width 2) "px"] [:& copy-button {:data (copy-data selrect :width)}]] [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.height")] - [:div.attributes-value height "px"] + [:div.attributes-value (ust/format-precision height 2) "px"] [:& copy-button {:data (copy-data selrect :height)}]] (when (not= (:x shape) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.left")] - [:div.attributes-value x "px"] + [:div.attributes-value (ust/format-precision x 2) "px"] [:& copy-button {:data (copy-data selrect :x)}]]) (when (not= (:y shape) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.top")] - [:div.attributes-value y "px"] + [:div.attributes-value (ust/format-precision y 2) "px"] [:& copy-button {:data (copy-data selrect :y)}]]) (when (ctr/radius-1? shape) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.radius")] - [:div.attributes-value (:rx shape 0) "px"] + [:div.attributes-value (ust/format-precision (:rx shape 0) 2) "px"] [:& copy-button {:data (copy-data shape :rx)}]]) (when (ctr/radius-4? shape) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.radius")] [:div.attributes-value - (:r1 shape) ", " - (:r2 shape) ", " - (:r3 shape) ", " - (:r4 shape) "px"] + (ust/format-precision (:r1 shape) 2) ", " + (ust/format-precision (:r2 shape) 2) ", " + (ust/format-precision (:r3 shape) 2) ", " + (ust/format-precision (:r4 shape) 2) "px"] [:& copy-button {:data (copy-data shape :r1)}]]) (when (not= (:rotation shape 0) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.rotation")] - [:div.attributes-value (:rotation shape) "deg"] + [:div.attributes-value (ust/format-precision (:rotation shape) 2) "deg"] [:& copy-button {:data (copy-data shape :rotation)}]])])) From 7185199d05da8db15f5d00de90dd30e2198d5bcb Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 4 May 2022 16:26:31 +0200 Subject: [PATCH 02/98] :bug: Fix feedback crash --- backend/src/app/http/feedback.clj | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/backend/src/app/http/feedback.clj b/backend/src/app/http/feedback.clj index d5b6423e7..839df124d 100644 --- a/backend/src/app/http/feedback.clj +++ b/backend/src/app/http/feedback.clj @@ -7,6 +7,7 @@ (ns app.http.feedback "A general purpose feedback module." (:require + [app.common.data :as d] [app.common.exceptions :as ex] [app.common.spec :as us] [app.config :as cf] @@ -44,7 +45,8 @@ [{:keys [pool] :as cfg} {:keys [profile-id] :as request}] (let [ftoken (cf/get :feedback-token ::no-token) token (yrq/get-header request "x-feedback-token") - params (::yrq/params request)] + params (d/merge (:params request) + (:body-params request))] (cond (uuid? profile-id) (let [profile (profile/retrieve-profile-data pool profile-id) From 053975ef82484c69a62e56678302f30f073fa7a7 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 4 May 2022 16:38:10 +0200 Subject: [PATCH 03/98] Fix members menu popup is not correctly aligned --- frontend/resources/styles/main/partials/dashboard-team.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/resources/styles/main/partials/dashboard-team.scss b/frontend/resources/styles/main/partials/dashboard-team.scss index f018f95f5..6bab8a4c3 100644 --- a/frontend/resources/styles/main/partials/dashboard-team.scss +++ b/frontend/resources/styles/main/partials/dashboard-team.scss @@ -195,7 +195,7 @@ border-radius: 4px; box-shadow: 0px 2px 8px rgba(0, 0, 0, 0.25); z-index: 12; - top: 40px; + top: 30px; left: 6px; width: 155px; From 82185794a84eaf17b02655a7ede50c2fa9edbd18 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 4 May 2022 17:08:31 +0200 Subject: [PATCH 04/98] :bug: Fix shapes filter --- frontend/src/app/main/ui/workspace/sidebar/layers.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index bec953870..948cfdb6f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -368,7 +368,10 @@ search-and-filters (fn [[id shape]] (let [search (:search-text @filter-state) - filters (:active-filters @filter-state)] + filters (:active-filters @filter-state) + filters (cond-> filters + (some #{:shape} filters) + (conj :rect :circle :path :bool))] (or (= uuid/zero id) (and From fe6e62482abee1ff341f4a22cc014672cd2eb4e7 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 4 May 2022 18:00:44 +0200 Subject: [PATCH 05/98] :bug: Fix bad texts in layers filter pills --- .../src/app/main/ui/workspace/sidebar/layers.cljs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 948cfdb6f..62afe4a1f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -434,8 +434,17 @@ [:div.active-filters (for [f (:active-filters @filter-state)] - [:span {:on-click (remove-filter f)} - (tr f) i/cross])] + (let [name (case f + :frame (tr "workspace.sidebar.layers.frames") + :group (tr "workspace.sidebar.layers.groups") + :mask (tr "workspace.sidebar.layers.masks") + :component (tr "workspace.sidebar.layers.components") + :text (tr "workspace.sidebar.layers.texts") + :image (tr "workspace.sidebar.layers.images") + :shape (tr "workspace.sidebar.layers.shapes") + (tr f))] + [:span {:on-click (remove-filter f)} + name i/cross]))] (when (:show-filters-menu @filter-state) [:div.filters-container From 430ccda02cdc5d8b1a40c906e428a6b615d06e9c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 13:03:36 +0200 Subject: [PATCH 06/98] :bug: Fix problem with black frame background --- .../app/main/ui/workspace/shapes/frame/thumbnail_render.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index d0b987033..783fa222e 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -23,7 +23,7 @@ canvas-height (.-height canvas-node)] (.clearRect canvas-context 0 0 canvas-width canvas-height) (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - (.toDataURL canvas-node "image/jpeg" 0.8))) + (.toDataURL canvas-node "image/png"))) (defn use-render-thumbnail "Hook that will create the thumbnail thata" From 0db2f87e3e327dd3d73708893b8604e923ec627a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 13:11:03 +0200 Subject: [PATCH 07/98] :bug: Fix problems with thumbnails generation --- .../src/app/main/ui/workspace/shapes.cljs | 2 +- .../app/main/ui/workspace/shapes/frame.cljs | 8 +-- .../shapes/frame/thumbnail_render.cljs | 61 +++++++++++-------- .../src/app/main/ui/workspace/viewport.cljs | 4 +- .../app/main/ui/workspace/viewport/hooks.cljs | 35 ++++------- .../app/main/ui/workspace/viewport/utils.cljs | 2 +- 6 files changed, 55 insertions(+), 57 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes.cljs b/frontend/src/app/main/ui/workspace/shapes.cljs index 78cb6ab8b..7905df951 100644 --- a/frontend/src/app/main/ui/workspace/shapes.cljs +++ b/frontend/src/app/main/ui/workspace/shapes.cljs @@ -65,7 +65,7 @@ [:& frame-wrapper {:shape item :key (:id item) :objects (get frame-objects (:id item)) - :thumbnail? (not (get active-frames (:id item) false))}] + :thumbnail? (not (contains? active-frames (:id item)))}] [:& shape-wrapper {:shape item :key (:id item)}]))])) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 22991ac90..e74c3b56a 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -114,10 +114,10 @@ (when (not @rendered?) (reset! rendered? true))))) [:g.frame-container {:key "frame-container" :ref on-frame-load} - thumb-renderer - - [:g.frame-thumbnail + [:g.frame-thumbnail {:id (dm/str "thumbnail-container-" (:id shape))} [:> frame/frame-thumbnail {:key (dm/str (:id shape)) :shape (cond-> shape (some? thumbnail-data) - (assoc :thumbnail thumbnail-data))}]]])))) + (assoc :thumbnail thumbnail-data))}] + + thumb-renderer]])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 783fa222e..15adf671c 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -14,6 +14,7 @@ [app.util.dom :as dom] [app.util.object :as obj] [app.util.timers :as ts] + [beicon.core :as rx] [rumext.alpha :as mf])) (defn- draw-thumbnail-canvas @@ -44,43 +45,51 @@ thumbnail-ref? (mf/use-var thumbnail?) + updates-str (mf/use-memo #(rx/subject)) + on-image-load (mf/use-callback (fn [] - (let [canvas-node (mf/ref-val frame-canvas-ref) - img-node (mf/ref-val frame-image-ref) - thumb-data (draw-thumbnail-canvas canvas-node img-node)] - (st/emit! (dw/update-thumbnail id thumb-data)) - (reset! image-url nil)))) + (ts/raf + #(let [canvas-node (mf/ref-val frame-canvas-ref) + img-node (mf/ref-val frame-image-ref) + thumb-data (draw-thumbnail-canvas canvas-node img-node)] + (st/emit! (dw/update-thumbnail id thumb-data)) + (reset! image-url nil))))) - on-change - (mf/use-callback - (fn [] - (when (and (some? @node-ref) (not @disable-ref?)) - (let [node @node-ref] - (ts/schedule-on-idle - #(let [frame-html (dom/node->xml node) - {:keys [x y width height]} @shape-ref - svg-node - (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") - (dom/set-property! "version" "1.1") - (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) - (dom/set-property! "width" width) - (dom/set-property! "height" height) - (dom/set-property! "fill" "none") - (obj/set! "innerHTML" frame-html)) - img-src (-> svg-node dom/node->xml dom/svg->data-uri)] - (reset! image-url img-src))))))) + on-update-frame + (fn [] + (when (and (some? @node-ref) (not @disable-ref?)) + (let [node @node-ref + frame-html (dom/node->xml node) + {:keys [x y width height]} @shape-ref + svg-node + (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") + (dom/set-property! "version" "1.1") + (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) + (dom/set-property! "width" width) + (dom/set-property! "height" height) + (dom/set-property! "fill" "none") + (obj/set! "innerHTML" frame-html)) + img-src (-> svg-node dom/node->xml dom/svg->data-uri)] + (reset! image-url img-src)))) on-load-frame-dom (mf/use-callback (fn [node] (when (and (some? node) (nil? @observer-ref)) - (on-change []) - (let [observer (js/MutationObserver. on-change)] + (rx/push! updates-str :update) + (let [observer (js/MutationObserver. (partial rx/push! updates-str))] (.observe observer node #js {:childList true :attributes true :characterData true :subtree true}) (reset! observer-ref observer)))))] + (mf/use-effect + (fn [] + (let [subid (->> updates-str + (rx/debounce 200) + (rx/subs on-update-frame))] + #(rx/dispose! subid)))) + (mf/use-effect (mf/deps disable?) (fn [] @@ -104,7 +113,7 @@ [on-load-frame-dom (when (some? @image-url) (mf/html - [:g.thumbnail-rendering {:opacity 0} + [:g.thumbnail-rendering [:foreignObject {:x x :y y :width width :height height} [:canvas {:ref frame-canvas-ref :width fixed-width diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0e5c2ed63..715543c84 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -90,7 +90,7 @@ hover (mf/use-state nil) hover-disabled? (mf/use-state false) frame-hover (mf/use-state nil) - active-frames (mf/use-state {}) + active-frames (mf/use-state #{}) ;; REFS viewport-ref (mf/use-ref nil) @@ -183,7 +183,7 @@ (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom) (hooks/setup-viewport-modifiers modifiers base-objects) (hooks/setup-shortcuts node-editing? drawing-path?) - (hooks/setup-active-frames base-objects vbox hover active-frames zoom) + (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom) [:div.viewport [:div.viewport-overlays {:ref overlays-ref} diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index a8890c0b4..9f656d1de 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -219,32 +219,21 @@ (gsh/overlaps? frame vbox)))) (defn setup-active-frames - [objects vbox hover active-frames zoom] + [objects hover-ids selected active-frames zoom] (mf/use-effect - (mf/deps vbox) - + (mf/deps objects @hover-ids selected zoom) (fn [] - (swap! active-frames - (fn [active-frames] - (let [set-active-frames - (fn [active-frames id active?] - (cond-> active-frames - (and active? (inside-vbox vbox objects id)) - (assoc id true)))] - (reduce-kv set-active-frames {} active-frames)))))) - - (mf/use-effect - (mf/deps @hover @active-frames zoom) - (fn [] - (let [frame-id (if (= :frame (:type @hover)) - (:id @hover) - (:frame-id @hover))] - (if (< zoom 0.25) - (when (some? @active-frames) - (reset! active-frames nil)) - (when (and (some? frame-id)(not (contains? @active-frames frame-id))) - (reset! active-frames {frame-id true}))))))) + (when (some? @hover-ids) + (let [hover-frame (when (> zoom 0.25) (last @hover-ids)) + new-active-frames (if (some? hover-frame) #{hover-frame} #{}) + new-active-frames + (into new-active-frames + (comp + (filter #(not= :frame (get-in objects [% :type]))) + (map #(get-in objects [% :frame-id]))) + selected) ] + (reset! active-frames new-active-frames)))))) ;; NOTE: this is executed on each page change, maybe we need to move ;; this shortcuts outside the viewport? diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 69a53474f..fe5c4586e 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -86,7 +86,7 @@ mask? (and group? masked-group?) ;; When the shape is a frame we maybe need to move its thumbnail - thumb-node (when frame? (dom/query (str "#thumbnail-" id)))] + thumb-node (when frame? (dom/query (str "#thumbnail-container-" id)))] (cond frame? From 1d39bbaa3c10ecf8365679f855afbb4e925e37eb Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Thu, 5 May 2022 13:45:55 +0200 Subject: [PATCH 08/98] :bug: Do not show team-up modal for users already on a team --- CHANGES.md | 1 + .../app/main/ui/onboarding/team_choice.cljs | 39 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 09a3ab9d2..5e4f2f82e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -48,6 +48,7 @@ ### :bug: Bugs fixed +- Do not show team-up modal for users already on a team [Taiga #3311](https://tree.taiga.io/project/penpot/issue/3311) - Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069) - Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221) - Round the size values on handoff to two decimals [Taiga #3227](https://tree.taiga.io/project/penpot/issue/3227) diff --git a/frontend/src/app/main/ui/onboarding/team_choice.cljs b/frontend/src/app/main/ui/onboarding/team_choice.cljs index 23773ba76..3df054a83 100644 --- a/frontend/src/app/main/ui/onboarding/team_choice.cljs +++ b/frontend/src/app/main/ui/onboarding/team_choice.cljs @@ -10,6 +10,7 @@ [app.main.data.dashboard :as dd] [app.main.data.messages :as dm] [app.main.data.modal :as modal] + [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.forms :as fm] [app.util.i18n :as i18n :refer [tr]] @@ -37,25 +38,27 @@ on-team-up (fn [] (st/emit! (modal/show {:type :onboarding-team}))) - ] + teams (mf/deref refs/teams)] - [:div.modal-overlay - [:div.modal-container.onboarding.final.animated.fadeInUp - [:div.modal-top - [:h1 {:data-test "onboarding-welcome-title"} (tr "onboarding.welcome.title")] - [:p (tr "onboarding.welcome.desc3")]] - [:div.modal-columns - [:div.modal-left - [:div.content-button {:on-click on-fly-solo - :data-test "fly-solo-op"} - [:h2 (tr "onboarding.choice.fly-solo")] - [:p (tr "onboarding.choice.fly-solo-desc")]]] - [:div.modal-right - [:div.content-button {:on-click on-team-up :data-test "team-up-button"} - [:h2 (tr "onboarding.choice.team-up")] - [:p (tr "onboarding.choice.team-up-desc")]]]] - [:img.deco {:src "images/deco-left.png" :border "0"}] - [:img.deco.right {:src "images/deco-right.png" :border "0"}]]])) + (if (< (count teams) 2) + [:div.modal-overlay + [:div.modal-container.onboarding.final.animated.fadeInUp + [:div.modal-top + [:h1 {:data-test "onboarding-welcome-title"} (tr "onboarding.welcome.title")] + [:p (tr "onboarding.welcome.desc3")]] + [:div.modal-columns + [:div.modal-left + [:div.content-button {:on-click on-fly-solo + :data-test "fly-solo-op"} + [:h2 (tr "onboarding.choice.fly-solo")] + [:p (tr "onboarding.choice.fly-solo-desc")]]] + [:div.modal-right + [:div.content-button {:on-click on-team-up :data-test "team-up-button"} + [:h2 (tr "onboarding.choice.team-up")] + [:p (tr "onboarding.choice.team-up-desc")]]]] + [:img.deco {:src "images/deco-left.png" :border "0"}] + [:img.deco.right {:src "images/deco-right.png" :border "0"}]]] + [:div {:on-load on-fly-solo}]))) (mf/defc onboarding-team-modal {::mf/register modal/components From baf3f7ea158926da6461e8f0ca547d6fd03d8d86 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 14:24:14 +0200 Subject: [PATCH 09/98] :bug: Fix problem with outerstrokes for frames --- frontend/src/app/main/ui/shapes/frame.cljs | 44 +++++++++++++++------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 0d371ce49..91425cd79 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -45,15 +45,32 @@ [props] (let [shape (obj/get props "shape")] (when (:thumbnail shape) - [:image.frame-thumbnail - {:id (dm/str "thumbnail-" (:id shape)) - :xlinkHref (:thumbnail shape) - :x (:x shape) - :y (:y shape) - :width (:width shape) - :height (:height shape) - ;; DEBUG - :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]))) + (let [{:keys [x y width height]} shape + transform (gsh/transform-matrix shape) + props (-> (attrs/extract-style-attrs shape) + (obj/merge! + #js {:x x + :y y + :transform (str transform) + :width width + :height height + :className "frame-background"})) + path? (some? (.-d props))] + [:* + [:image.frame-thumbnail + {:id (dm/str "thumbnail-" (:id shape)) + :xlinkHref (:thumbnail shape) + :x (:x shape) + :y (:y shape) + :width (:width shape) + :height (:height shape) + ;; DEBUG + :style {:filter (when (debug? :thumbnails) "sepia(1)")}}] + + [:& shape-strokes {:shape shape} + (if path? + [:> :path props] + [:> :rect props])]])))) (defn frame-shape [shape-wrapper] @@ -88,8 +105,9 @@ (for [item childs] [:& shape-wrapper {:shape item :key (dm/str (:id item))}]) - [:& shape-strokes {:shape shape} - (if path? - [:> :path props] - [:> :rect props])]]]]))) + ]] + [:& shape-strokes {:shape shape} + (if path? + [:> :path props] + [:> :rect props])]]))) From 9099403421337f0dc8cad8196814e7ea96cded63 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 16:46:21 +0200 Subject: [PATCH 10/98] :bug: Improved resilience for thumbnail generation --- .../shapes/frame/thumbnail_render.cljs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 15adf671c..5aa7489b6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -19,12 +19,17 @@ (defn- draw-thumbnail-canvas [canvas-node img-node] - (let [canvas-context (.getContext canvas-node "2d") - canvas-width (.-width canvas-node) - canvas-height (.-height canvas-node)] - (.clearRect canvas-context 0 0 canvas-width canvas-height) - (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - (.toDataURL canvas-node "image/png"))) + (try + (when (and (some? canvas-node) (some? img-node)) + (let [canvas-context (.getContext canvas-node "2d") + canvas-width (.-width canvas-node) + canvas-height (.-height canvas-node)] + (.clearRect canvas-context 0 0 canvas-width canvas-height) + (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (.toDataURL canvas-node "image/png"))) + (catch :default err + (.error js/console err) + nil))) (defn use-render-thumbnail "Hook that will create the thumbnail thata" @@ -54,8 +59,9 @@ #(let [canvas-node (mf/ref-val frame-canvas-ref) img-node (mf/ref-val frame-image-ref) thumb-data (draw-thumbnail-canvas canvas-node img-node)] - (st/emit! (dw/update-thumbnail id thumb-data)) - (reset! image-url nil))))) + (when (some? thumb-data) + (st/emit! (dw/update-thumbnail id thumb-data)) + (reset! image-url nil)))))) on-update-frame (fn [] From 1723ff1da5aeabecafdce554f2bf2ab06cf8deb3 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 17:04:03 +0200 Subject: [PATCH 11/98] :bug: Numeric input for font size --- .../main/ui/components/editable_select.cljs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/components/editable_select.cljs b/frontend/src/app/main/ui/components/editable_select.cljs index 9d4101bea..ebc5e5589 100644 --- a/frontend/src/app/main/ui/components/editable_select.cljs +++ b/frontend/src/app/main/ui/components/editable_select.cljs @@ -10,6 +10,7 @@ [app.common.math :as mth] [app.common.uuid :as uuid] [app.main.ui.components.dropdown :refer [dropdown]] + [app.main.ui.components.numeric-input :refer [numeric-input]] [app.main.ui.icons :as i] [app.util.dom :as dom] [app.util.keyboard :as kbd] @@ -141,13 +142,19 @@ [:div.editable-select {:class class :ref on-node-load} - [:input.input-text {:value (or (some-> @state :current-value value->label) "") - :on-change handle-change-input - :on-key-down handle-key-down - :on-focus handle-focus - :on-blur handle-blur - :placeholder placeholder - :type type}] + (if (= type "number") + [:> numeric-input {:value (or (some-> @state :current-value value->label) "") + :on-change set-value + :on-focus handle-focus + :on-blur handle-blur + :placeholder placeholder}] + [:input.input-text {:value (or (some-> @state :current-value value->label) "") + :on-change handle-change-input + :on-key-down handle-key-down + :on-focus handle-focus + :on-blur handle-blur + :placeholder placeholder + :type type}]) [:span.dropdown-button {:on-click open-dropdown} i/arrow-down] [:& dropdown {:show (get @state :is-open? false) From 5b37c1122160451419256150bf0ac333fb1fdd9c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 17:16:05 +0200 Subject: [PATCH 12/98] :bug: Fix letter spacing for svg texts --- frontend/src/app/main/ui/shapes/text/svg_text.cljs | 1 + frontend/src/app/util/text_svg_position.cljs | 1 + 2 files changed, 2 insertions(+) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 03cfd7235..7d2cc1b61 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -82,6 +82,7 @@ :fontWeight (:font-weight data) :textTransform (:text-transform data) :textDecoration (:text-decoration data) + :letterSpacing (:letter-spacing data) :fontStyle (:font-style data) :direction (if (:rtl data) "rtl" "ltr") :whiteSpace "pre"} diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index b8a8a4f94..d88039196 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -119,6 +119,7 @@ :font-weight (str (get "font-weight")) :text-transform (str (get "text-transform")) :text-decoration (str (get "text-decoration")) + :letter-spacing (str (get "letter-spacing")) :font-style (str (get "font-style")) :fills (transit/decode-str (get "--fills")) :text text})))))))))) From 8fb22b8eeea9ef39db60ba77ad637fe2d477756e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Wed, 4 May 2022 15:53:02 +0200 Subject: [PATCH 13/98] :bug: Add a protection for some possible race condition --- .../app/main/data/workspace/libraries.cljs | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index b820336c6..be53b16d2 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -569,47 +569,48 @@ ptk/WatchEvent (watch [it state _] - (log/info :msg "SYNC-FILE" - :file (dwlh/pretty-file file-id state) - :library (dwlh/pretty-file library-id state)) - (let [file (wsh/get-file state file-id) + (when (and (some? file-id) (some? library-id)) ; Prevent race conditions while navigating out of the file + (log/info :msg "SYNC-FILE" + :file (dwlh/pretty-file file-id state) + :library (dwlh/pretty-file library-id state)) + (let [file (wsh/get-file state file-id) - library-changes (reduce - pcb/concat-changes - (pcb/empty-changes it) - [(dwlh/generate-sync-library it file-id :components library-id state) - (dwlh/generate-sync-library it file-id :colors library-id state) - (dwlh/generate-sync-library it file-id :typographies library-id state)]) - file-changes (reduce - pcb/concat-changes - (pcb/empty-changes it) - [(dwlh/generate-sync-file it file-id :components library-id state) - (dwlh/generate-sync-file it file-id :colors library-id state) - (dwlh/generate-sync-file it file-id :typographies library-id state)]) + library-changes (reduce + pcb/concat-changes + (pcb/empty-changes it) + [(dwlh/generate-sync-library it file-id :components library-id state) + (dwlh/generate-sync-library it file-id :colors library-id state) + (dwlh/generate-sync-library it file-id :typographies library-id state)]) + file-changes (reduce + pcb/concat-changes + (pcb/empty-changes it) + [(dwlh/generate-sync-file it file-id :components library-id state) + (dwlh/generate-sync-file it file-id :colors library-id state) + (dwlh/generate-sync-file it file-id :typographies library-id state)]) - changes (pcb/concat-changes library-changes file-changes)] + changes (pcb/concat-changes library-changes file-changes)] - (log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes - (:redo-changes changes) - file)) - (rx/concat - (rx/of (dm/hide-tag :sync-dialog)) - (when (seq (:redo-changes changes)) - (rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto - :file-id file-id)))) - (when (not= file-id library-id) - ;; When we have just updated the library file, give some time for the - ;; update to finish, before marking this file as synced. - ;; TODO: look for a more precise way of syncing this. - ;; Maybe by using the stream (second argument passed to watch) - ;; to wait for the corresponding changes-committed and then proceed - ;; with the :update-sync mutation. - (rx/concat (rx/timer 3000) - (rp/mutation :update-sync - {:file-id file-id - :library-id library-id}))) - (when (seq (:redo-changes library-changes)) - (rx/of (sync-file-2nd-stage file-id library-id)))))))) + (log/debug :msg "SYNC-FILE finished" :js/rchanges (log-changes + (:redo-changes changes) + file)) + (rx/concat + (rx/of (dm/hide-tag :sync-dialog)) + (when (seq (:redo-changes changes)) + (rx/of (dch/commit-changes (assoc changes ;; TODO a ver qué pasa con esto + :file-id file-id)))) + (when (not= file-id library-id) + ;; When we have just updated the library file, give some time for the + ;; update to finish, before marking this file as synced. + ;; TODO: look for a more precise way of syncing this. + ;; Maybe by using the stream (second argument passed to watch) + ;; to wait for the corresponding changes-committed and then proceed + ;; with the :update-sync mutation. + (rx/concat (rx/timer 3000) + (rp/mutation :update-sync + {:file-id file-id + :library-id library-id}))) + (when (seq (:redo-changes library-changes)) + (rx/of (sync-file-2nd-stage file-id library-id))))))))) (defn sync-file-2nd-stage "If some components have been modified, we need to launch another synchronization From 66f7d35510ce517a8950e6d534933d1895a557fd Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 5 May 2022 17:21:28 +0200 Subject: [PATCH 14/98] :bug: Fix problem with multi-line text and strokes --- frontend/src/app/main/ui/shapes/text/svg_text.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 7d2cc1b61..c13f048e6 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -89,5 +89,6 @@ (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) shape (assoc shape :fills (:fills data))] - [:& shape-custom-strokes {:shape shape :key index} - [:> :text props (:text data)]]))]])) + [:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} + [:& shape-custom-strokes {:shape shape :key index} + [:> :text props (:text data)]]]))]])) From b86ea5b5e2b0553fabe5a7172becf1976a1ba9fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 5 May 2022 12:11:31 +0200 Subject: [PATCH 15/98] :bug: Fix notifications of external library changes https://tree.taiga.io/project/penpot/issue/3348 --- frontend/src/app/main/data/workspace/notifications.cljs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/data/workspace/notifications.cljs b/frontend/src/app/main/data/workspace/notifications.cljs index 40f6f8ae1..fa88c508a 100644 --- a/frontend/src/app/main/data/workspace/notifications.cljs +++ b/frontend/src/app/main/data/workspace/notifications.cljs @@ -54,8 +54,8 @@ ;; Subscribe to notifications of the subscription (->> stream (rx/filter (ptk/type? ::dws/message)) - (rx/map deref) - (rx/filter #(= subs-id (:subs-id %))) + (rx/map deref) ;; :library-change events occur in a different file, but need to be processed anyway + (rx/filter #(or (= subs-id (:subs-id %)) (= (:type %) :library-change))) (rx/map process-message)) ;; On reconnect, send again the subscription messages From 7e05b7e6d9130cc83b35dab953c20eaef3a547c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 5 May 2022 16:02:39 +0200 Subject: [PATCH 16/98] :bug: Fix group typographies https://tree.taiga.io/project/penpot/issue/3338 --- .../app/main/data/workspace/libraries.cljs | 2 ++ .../sidebar/options/menus/typography.cljs | 27 +++---------------- 2 files changed, 6 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index be53b16d2..285e85523 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -221,6 +221,8 @@ ptk/WatchEvent (watch [it state _] (let [data (get state :workspace-data) + [path name] (cph/parse-path-name (:name typography)) + typography (assoc typography :path path :name name) changes (-> (pcb/empty-changes it) (pcb/with-library-data data) (pcb/update-typography typography))] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs index 3967aa506..9471d34bc 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/typography.cljs @@ -462,21 +462,13 @@ name-input-ref (mf/use-ref) on-change-ref (mf/use-ref nil) - name-ref (mf/use-ref (:name typography)) - on-name-blur (mf/use-callback (mf/deps on-change) (fn [event] - (let [content (dom/get-target-val event)] - (when-not (str/blank? content) - (let [[path name] (cph/parse-path-name content)] - (on-change {:name name :path path})))))) - - on-name-change - (mf/use-callback - (fn [event] - (mf/set-ref-val! name-ref (dom/get-target-val event))))] + (let [name (dom/get-target-val event)] + (when-not (str/blank? name) + (on-change {:name name })))))] (mf/use-effect (mf/deps editing?) @@ -498,16 +490,6 @@ (fn [] (mf/set-ref-val! on-change-ref {:on-change on-change}))) - (mf/use-effect - (fn [] - (fn [] - (let [content (mf/ref-val name-ref)] - ;; On destroy we check if it changed - (when (and (some? content) (not= content (:name typography))) - (let [{:keys [on-change]} (mf/ref-val on-change-ref) - [path name] (cph/parse-path-name content)] - (on-change {:name name :path path}))))))) - [:* [:div.element-set-options-group.typography-entry {:class (when selected? "selected") @@ -582,8 +564,7 @@ {:type "text" :ref name-input-ref :default-value (cph/merge-path-item (:path typography) (:name typography)) - :on-blur on-name-blur - :on-change on-name-change}] + :on-blur on-name-blur}] [:div.element-set-actions-button {:on-click #(reset! open? false)} From 9b8b6134c55d5cc672f84947ac1f8ddd61c5f4fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 6 May 2022 12:07:19 +0200 Subject: [PATCH 17/98] :bug: Allow images to adjust to the shape size https://tree.taiga.io/project/penpot/issue/3329 --- frontend/src/app/main/ui/shapes/fills.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/shapes/fills.cljs b/frontend/src/app/main/ui/shapes/fills.cljs index df285209e..45ba0586e 100644 --- a/frontend/src/app/main/ui/shapes/fills.cljs +++ b/frontend/src/app/main/ui/shapes/fills.cljs @@ -80,5 +80,6 @@ (when has-image? [:image {:xlinkHref (get embed uri uri) + :preserveAspectRatio "none" :width width :height height}])]])]))))) From 18afb701fbfdd3fab086945fdc68f0267a5ec041 Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 5 May 2022 14:39:33 +0200 Subject: [PATCH 18/98] :bug: Fix apply color to groups from assets panel --- .../app/main/ui/workspace/sidebar/assets.cljs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs index 255148cde..b33d37360 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets.cljs @@ -33,6 +33,7 @@ [app.main.ui.context :as ctx] [app.main.ui.icons :as i] [app.main.ui.workspace.sidebar.options.menus.typography :refer [typography-entry]] + [app.util.color :as uc] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] [app.util.i18n :as i18n :refer [tr]] @@ -802,10 +803,21 @@ ;; TODO: looks like the first argument is not necessary apply-color (fn [_ event] - (let [ids (wsh/lookup-selected @st/state)] + (let [objects (wsh/lookup-page-objects @st/state) + selected (->> (wsh/lookup-selected @st/state) + (cph/clean-loops objects)) + selected-obj (keep (d/getf objects) selected) + select-shapes-for-color (fn [shape objects] + (let [shapes (case (:type shape) + :group (cph/get-children objects (:id shape)) + [shape])] + (->> shapes + (remove cph/group-shape?) + (map :id)))) + ids (mapcat #(select-shapes-for-color % objects) selected-obj)] (if (kbd/alt? event) - (st/emit! (dc/change-stroke ids color 0)) - (st/emit! (dc/change-fill ids color 0))))) + (st/emit! (dc/change-stroke ids (merge uc/empty-color color) 0)) + (st/emit! (dc/change-fill ids (merge uc/empty-color color) 0))))) rename-color (fn [name] From 1102bc9cbad74af2dd39f2de1ac29d057d84a565 Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 5 May 2022 14:41:02 +0200 Subject: [PATCH 19/98] :bug: Activate button when input change in account --- frontend/src/app/main/ui/components/forms.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index ec49222ae..8b4723a28 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -79,6 +79,7 @@ on-focus #(reset! focus? true) on-change (fn [event] (let [value (-> event dom/get-target dom/get-input-value)] + (swap! form assoc-in [:touched input-name] true) (fm/on-input-change form input-name value trim))) on-blur From 37fdf51eafc88cf987bf5c122d70b4c5647431fb Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 5 May 2022 14:42:22 +0200 Subject: [PATCH 20/98] :bug: Fix copying layout values with only multiple decimals --- .../ui/viewer/handoff/attributes/layout.cljs | 23 ++++++++++--------- frontend/src/app/util/code_gen.cljs | 3 ++- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs index fe748716c..06e87bce7 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -8,9 +8,9 @@ (:require [app.common.spec.radius :as ctr] [app.main.ui.components.copy-button :refer [copy-button]] + [app.main.ui.formats :as fmt] [app.util.code-gen :as cg] [app.util.i18n :refer [tr]] - [app.util.strings :as ust] [cuerdas.core :as str] [rumext.alpha :as mf])) @@ -35,50 +35,51 @@ (mf/defc layout-block [{:keys [shape]}] (let [selrect (:selrect shape) + _ (prn shape) {:keys [width height x y]} selrect] [:* [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.width")] - [:div.attributes-value (ust/format-precision width 2) "px"] + [:div.attributes-value (fmt/format-pixels width)] [:& copy-button {:data (copy-data selrect :width)}]] [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.height")] - [:div.attributes-value (ust/format-precision height 2) "px"] + [:div.attributes-value (fmt/format-pixels height)] [:& copy-button {:data (copy-data selrect :height)}]] (when (not= (:x shape) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.left")] - [:div.attributes-value (ust/format-precision x 2) "px"] + [:div.attributes-value (fmt/format-pixels x)] [:& copy-button {:data (copy-data selrect :x)}]]) (when (not= (:y shape) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.top")] - [:div.attributes-value (ust/format-precision y 2) "px"] + [:div.attributes-value (fmt/format-pixels y)] [:& copy-button {:data (copy-data selrect :y)}]]) (when (ctr/radius-1? shape) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.radius")] - [:div.attributes-value (ust/format-precision (:rx shape 0) 2) "px"] + [:div.attributes-value (fmt/format-pixels (:rx shape 0))] [:& copy-button {:data (copy-data shape :rx)}]]) (when (ctr/radius-4? shape) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.radius")] [:div.attributes-value - (ust/format-precision (:r1 shape) 2) ", " - (ust/format-precision (:r2 shape) 2) ", " - (ust/format-precision (:r3 shape) 2) ", " - (ust/format-precision (:r4 shape) 2) "px"] + (fmt/format-number (:r1 shape)) ", " + (fmt/format-number (:r2 shape)) ", " + (fmt/format-number (:r3 shape))", " + (fmt/format-pixels (:r4 shape))] [:& copy-button {:data (copy-data shape :r1)}]]) (when (not= (:rotation shape 0) 0) [:div.attributes-unit-row [:div.attributes-label (tr "handoff.attributes.layout.rotation")] - [:div.attributes-value (ust/format-precision (:rotation shape) 2) "deg"] + [:div.attributes-value (fmt/format-number (:rotation shape)) "deg"] [:& copy-button {:data (copy-data shape :rotation)}]])])) diff --git a/frontend/src/app/util/code_gen.cljs b/frontend/src/app/util/code_gen.cljs index 3c9162378..dbaf51da2 100644 --- a/frontend/src/app/util/code_gen.cljs +++ b/frontend/src/app/util/code_gen.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.text :as txt] + [app.main.ui.formats :as fmt] [app.util.color :as uc] [cuerdas.core :as str])) @@ -108,7 +109,7 @@ (every? #(or (nil? %) (= % 0)) value) (or (nil? value) (= value 0)))) - default-format (fn [value] (str value "px")) + default-format (fn [value] (str (fmt/format-pixels value))) format-property (fn [prop] (let [css-prop (or (prop to-prop) (name prop)) format-fn (or (prop format) default-format) From afa1af6dc266b04fececc43996598ad7dd41a788 Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 5 May 2022 14:42:43 +0200 Subject: [PATCH 21/98] :bug: Fix comments in viewer mode --- frontend/src/app/main/ui/viewer/comments.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/viewer/comments.cljs b/frontend/src/app/main/ui/viewer/comments.cljs index 7d547383d..44cd264af 100644 --- a/frontend/src/app/main/ui/viewer/comments.cljs +++ b/frontend/src/app/main/ui/viewer/comments.cljs @@ -144,8 +144,9 @@ :key (:seqn item)}])) (when-let [id (:open cstate)] - (when-let [thread (-> (get threads-map id) - (update :position gpt/transform modifier1))] + (when-let [thread (as-> (get threads-map id) $ + (when (some? $) + (update $ :position gpt/transform modifier1)))] [:& cmt/thread-comments {:thread thread :users users :zoom zoom}])) From 462eabd8a15a3456ef2ab2b4685121f9fdc08b46 Mon Sep 17 00:00:00 2001 From: Eva Date: Thu, 5 May 2022 15:01:27 +0200 Subject: [PATCH 22/98] :bug: Show '--' when multiple rotations --- frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs | 1 - .../app/main/ui/workspace/sidebar/options/menus/measures.cljs | 1 - 2 files changed, 2 deletions(-) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs index 06e87bce7..f2fca42b8 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/layout.cljs @@ -35,7 +35,6 @@ (mf/defc layout-block [{:keys [shape]}] (let [selrect (:selrect shape) - _ (prn shape) {:keys [width height x y]} selrect] [:* [:div.attributes-unit-row diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index d59207754..c581c2117 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -279,7 +279,6 @@ {:no-validate true :min 0 :max 359 - :default 0 :data-wrap true :placeholder "--" :on-click select-all From d9a9eb372919830f300713e309a1e26b3ec8f4d2 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Thu, 5 May 2022 17:11:45 +0200 Subject: [PATCH 23/98] :sparkles: Add icon to artboard thumbnail --- .../resources/images/icons/set-thumbnail.svg | 3 + .../styles/main/partials/workspace.scss | 4 ++ frontend/src/app/main/ui/icons.cljs | 1 + .../main/ui/workspace/viewport/widgets.cljs | 68 +++++++++++-------- 4 files changed, 49 insertions(+), 27 deletions(-) create mode 100644 frontend/resources/images/icons/set-thumbnail.svg diff --git a/frontend/resources/images/icons/set-thumbnail.svg b/frontend/resources/images/icons/set-thumbnail.svg new file mode 100644 index 000000000..c90cf5c85 --- /dev/null +++ b/frontend/resources/images/icons/set-thumbnail.svg @@ -0,0 +1,3 @@ + + + diff --git a/frontend/resources/styles/main/partials/workspace.scss b/frontend/resources/styles/main/partials/workspace.scss index 48b943526..320c6f8d9 100644 --- a/frontend/resources/styles/main/partials/workspace.scss +++ b/frontend/resources/styles/main/partials/workspace.scss @@ -316,6 +316,10 @@ $height-palette-max: 80px; } } +.workspace-frame-icon { + fill: $color-gray-40; +} + .workspace-frame-label { fill: $color-gray-40; font-size: $fs12; diff --git a/frontend/src/app/main/ui/icons.cljs b/frontend/src/app/main/ui/icons.cljs index c10126c61..4c0fc2585 100644 --- a/frontend/src/app/main/ui/icons.cljs +++ b/frontend/src/app/main/ui/icons.cljs @@ -138,6 +138,7 @@ (def ruler (icon-xref :ruler)) (def ruler-tool (icon-xref :ruler-tool)) (def search (icon-xref :search)) +(def set-thumbnail (icon-xref :set-thumbnail)) (def shape-halign-center (icon-xref :shape-halign-center)) (def shape-halign-left (icon-xref :shape-halign-left)) (def shape-halign-right (icon-xref :shape-halign-right)) diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index a9661c26f..1fe11720e 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -108,19 +108,19 @@ on-double-click (mf/use-callback - (mf/deps (:id frame)) - (st/emitf (dw/go-to-layout :layers) - (dw/start-rename-shape (:id frame)))) + (mf/deps (:id frame)) + (st/emitf (dw/go-to-layout :layers) + (dw/start-rename-shape (:id frame)))) on-context-menu (mf/use-callback - (mf/deps frame) - (fn [bevent] - (let [event (.-nativeEvent bevent) - position (dom/get-client-position event)] - (dom/prevent-default event) - (dom/stop-propagation event) - (st/emit! (dw/show-shape-context-menu {:position position :shape frame}))))) + (mf/deps frame) + (fn [bevent] + (let [event (.-nativeEvent bevent) + position (dom/get-client-position event)] + (dom/prevent-default event) + (dom/stop-propagation event) + (st/emit! (dw/show-shape-context-menu {:position position :shape frame}))))) on-pointer-enter (mf/use-callback @@ -132,24 +132,38 @@ (mf/use-callback (mf/deps (:id frame) on-frame-leave) (fn [_] - (on-frame-leave (:id frame))))] + (on-frame-leave (:id frame)))) + text-pos-x (if (:use-for-thumbnail? frame) 15 0)] - [:text {:x 0 - :y 0 - :width width - :height 20 - :class "workspace-frame-label" - :transform (str (when (and selected? modifiers) - (str (:displacement modifiers) " " )) - (text-transform label-pos zoom)) - :style {:fill (when selected? "var(--color-primary-dark)")} - :visibility (if show-artboard-names? "visible" "hidden") - :on-mouse-down on-mouse-down - :on-double-click on-double-click - :on-context-menu on-context-menu - :on-pointer-enter on-pointer-enter - :on-pointer-leave on-pointer-leave} - (:name frame)])) + [:* + (when (:use-for-thumbnail? frame) + [:g {:transform (str (when (and selected? modifiers) + (str (:displacement modifiers) " ")) + (text-transform label-pos zoom))} + [:svg {:x 0 + :y -9 + :width 12 + :height 12 + :class "workspace-frame-icon" + :style {:fill (when selected? "var(--color-primary-dark)")} + :visibility (if show-artboard-names? "visible" "hidden")} + [:use {:xlinkHref "#icon-set-thumbnail"}]]]) + [:text {:x text-pos-x + :y 0 + :width width + :height 20 + :class "workspace-frame-label" + :transform (str (when (and selected? modifiers) + (str (:displacement modifiers) " ")) + (text-transform label-pos zoom)) + :style {:fill (when selected? "var(--color-primary-dark)")} + :visibility (if show-artboard-names? "visible" "hidden") + :on-mouse-down on-mouse-down + :on-double-click on-double-click + :on-context-menu on-context-menu + :on-pointer-enter on-pointer-enter + :on-pointer-leave on-pointer-leave} + (:name frame)]])) (mf/defc frame-titles {::mf/wrap-props false} From 96aa756eb6b623ffb49d30da083aee840531bab6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Mon, 9 May 2022 12:46:52 +0200 Subject: [PATCH 24/98] :bug: Fix import svg with exterior strokes --- frontend/src/app/main/render.cljs | 2 +- frontend/src/app/main/ui/shapes/filters.cljs | 33 +++++++++++--------- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 8e3e77da5..31f07b6f7 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -189,7 +189,7 @@ (defn get-object-bounds [objects object-id] (let [object (get objects object-id) - padding (filters/calculate-padding object) + padding (filters/calculate-padding object true) bounds (-> (filters/get-filters-bounds object) (update :x - (:horizontal padding)) (update :y - (:vertical padding)) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index 378a22397..b5cf278f2 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -200,25 +200,30 @@ :width (- x2 x1) :height (- y2 y1)}))))) -(defn calculate-padding [shape] - (let [stroke-width (apply max 0 (map #(case (:stroke-alignment % :center) - :center (/ (:stroke-width % 0) 2) - :outer (:stroke-width % 0) - 0) (:strokes shape))) +(defn calculate-padding + ([shape] + (calculate-padding shape false)) - margin (apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape))) + ([shape ignore-margin?] + (let [stroke-width (apply max 0 (map #(case (:stroke-alignment % :center) + :center (/ (:stroke-width % 0) 2) + :outer (:stroke-width % 0) + 0) (:strokes shape))) + margin (if ignore-margin? + 0 + (apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape)))) - shadow-width (apply max 0 (map #(case (:style % :drop-shadow) - :drop-shadow (+ (mth/abs (:offset-x %)) (* (:spread %) 2) (* (:blur %) 2) 10) - 0) (:shadow shape))) + shadow-width (apply max 0 (map #(case (:style % :drop-shadow) + :drop-shadow (+ (mth/abs (:offset-x %)) (* (:spread %) 2) (* (:blur %) 2) 10) + 0) (:shadow shape))) - shadow-height (apply max 0 (map #(case (:style % :drop-shadow) - :drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10) - 0) (:shadow shape)))] + shadow-height (apply max 0 (map #(case (:style % :drop-shadow) + :drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10) + 0) (:shadow shape)))] - {:horizontal (+ stroke-width margin shadow-width) - :vertical (+ stroke-width margin shadow-height)})) + {:horizontal (+ stroke-width margin shadow-width) + :vertical (+ stroke-width margin shadow-height)}))) (defn change-filter-in "Adds the previous filter as `filter-in` parameter" From 8cd7f61150189654713404fe57b98187210a47ca Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 12:35:34 +0200 Subject: [PATCH 25/98] :bug: Fix problem with duplicated ids for thumbnails --- backend/src/app/migrations.clj | 3 ++ .../0072-mod-file-object-thumbnail-table.sql | 4 ++ backend/src/app/rpc/mutations/files.clj | 2 +- backend/src/app/rpc/queries/files.clj | 27 +++++++------ backend/src/app/tasks/file_gc.clj | 15 ++++--- backend/test/app/services_files_test.clj | 8 ++-- .../app/main/data/workspace/thumbnails.cljs | 40 +++++++------------ frontend/src/app/main/refs.cljs | 7 +++- .../app/main/ui/workspace/shapes/frame.cljs | 8 +++- .../ui/workspace/shapes/frame/node_store.cljs | 1 - .../shapes/frame/thumbnail_render.cljs | 4 +- frontend/src/app/worker/thumbnails.cljs | 10 +++-- frontend/src/debug.cljs | 3 ++ 13 files changed, 74 insertions(+), 58 deletions(-) create mode 100644 backend/src/app/migrations/sql/0072-mod-file-object-thumbnail-table.sql diff --git a/backend/src/app/migrations.clj b/backend/src/app/migrations.clj index d81001a1b..bc9f6c94f 100644 --- a/backend/src/app/migrations.clj +++ b/backend/src/app/migrations.clj @@ -223,6 +223,9 @@ {:name "0071-add-file-object-thumbnail-table" :fn (mg/resource "app/migrations/sql/0071-add-file-object-thumbnail-table.sql")} + + {:name "0072-mod-file-object-thumbnail-table" + :fn (mg/resource "app/migrations/sql/0072-mod-file-object-thumbnail-table.sql")} ]) diff --git a/backend/src/app/migrations/sql/0072-mod-file-object-thumbnail-table.sql b/backend/src/app/migrations/sql/0072-mod-file-object-thumbnail-table.sql new file mode 100644 index 000000000..e73459baf --- /dev/null +++ b/backend/src/app/migrations/sql/0072-mod-file-object-thumbnail-table.sql @@ -0,0 +1,4 @@ +TRUNCATE TABLE file_object_thumbnail; + +ALTER TABLE file_object_thumbnail + ALTER COLUMN object_id TYPE text; diff --git a/backend/src/app/rpc/mutations/files.clj b/backend/src/app/rpc/mutations/files.clj index a7bc61238..796d0dd5a 100644 --- a/backend/src/app/rpc/mutations/files.clj +++ b/backend/src/app/rpc/mutations/files.clj @@ -487,7 +487,7 @@ update set data = ?;") (s/def ::data (s/nilable ::us/string)) -(s/def ::object-id ::us/uuid) +(s/def ::object-id ::us/string) (s/def ::upsert-file-object-thumbnail (s/keys :req-un [::profile-id ::file-id ::object-id ::data])) diff --git a/backend/src/app/rpc/queries/files.clj b/backend/src/app/rpc/queries/files.clj index 3ce4846be..8efc073d3 100644 --- a/backend/src/app/rpc/queries/files.clj +++ b/backend/src/app/rpc/queries/files.clj @@ -197,13 +197,13 @@ (->> (db/exec! pool [sql file-id]) (d/index-by :object-id :data)))) - ([{:keys [pool]} file-id frame-ids] + ([{:keys [pool]} file-id object-ids] (with-open [conn (db/open pool)] (let [sql (str/concat "select object_id, data " " from file_object_thumbnail" " where file_id=? and object_id = ANY(?)") - ids (db/create-array conn "uuid" (seq frame-ids))] + ids (db/create-array conn "text" (seq object-ids))] (->> (db/exec! conn [sql file-id ids]) (d/index-by :object-id :data)))))) @@ -298,19 +298,21 @@ ;; function responsible of assoc available thumbnails ;; to frames and remove all children shapes from objects if ;; thumbnails is available - (assoc-thumbnails [objects thumbnails] + (assoc-thumbnails [objects page-id thumbnails] (loop [objects objects frames (filter cph/frame-shape? (vals objects))] - (if-let [{:keys [id] :as frame} (first frames)] - (let [frame (if-let [thumb (get thumbnails id)] + (if-let [frame (-> frames first)] + (let [frame-id (:id frame) + object-id (str page-id frame-id) + frame (if-let [thumb (get thumbnails object-id)] (assoc frame :thumbnail thumb :shapes []) (dissoc frame :thumbnail))] (if (:thumbnail frame) - (recur (-> (assoc objects id frame) - (d/without-keys (cph/get-children-ids objects id))) + (recur (-> (assoc objects frame-id frame) + (d/without-keys (cph/get-children-ids objects frame-id))) (rest frames)) - (recur (assoc objects id frame) + (recur (assoc objects frame-id frame) (rest frames)))) objects)))] @@ -319,10 +321,11 @@ frame-id (:id frame) page-id (or (:page-id frame) (-> data :pages first)) - page (dm/get-in data [:pages-index page-id]) - obj-ids (or (some-> frame-id list) - (map :id (cph/get-frames page))) + page (dm/get-in data [:pages-index page-id]) + frame-ids (if (some? frame) (list frame-id) (map :id (cph/get-frames (:objects page)))) + + obj-ids (map #(str page-id %) frame-ids) thumbs (retrieve-object-thumbnails cfg id obj-ids)] (cond-> page @@ -335,7 +338,7 @@ ;; Assoc the available thumbnails and prune not visible shapes ;; for avoid transfer unnecesary data. :always - (update :objects assoc-thumbnails thumbs))))) + (update :objects assoc-thumbnails page-id thumbs))))) (s/def ::file-data-for-thumbnail (s/keys :req-un [::profile-id ::file-id])) diff --git a/backend/src/app/tasks/file_gc.clj b/backend/src/app/tasks/file_gc.clj index 4591e56e8..029f0b7fe 100644 --- a/backend/src/app/tasks/file_gc.clj +++ b/backend/src/app/tasks/file_gc.clj @@ -12,6 +12,7 @@ (:require [app.common.data :as d] [app.common.logging :as l] + [app.common.pages.helpers :as cph] [app.common.pages.migrations :as pmg] [app.db :as db] [app.util.blob :as blob] @@ -125,10 +126,14 @@ {:columns [:object-id]}) (into #{} (map :object-id))) - using (->> (concat (vals (:pages-index data)) - (vals (:components data))) - (into #{} (comp (map :objects) - (mapcat keys)))) + get-objects-ids + (fn [{:keys [id objects]}] + (->> (cph/get-frames objects) + (map #(str id (:id %))))) + + using (into #{} + (mapcat get-objects-ids) + (vals (:pages-index data))) unused (set/difference stored using)] @@ -136,7 +141,7 @@ (let [sql (str/concat "delete from file_object_thumbnail " " where file_id=? and object_id=ANY(?)") - res (db/exec-one! conn [sql file-id (db/create-array conn "uuid" unused)])] + res (db/exec-one! conn [sql file-id (db/create-array conn "text" unused)])] (l/debug :hint "delete object thumbnails" :total (:next.jdbc/update-count res)))))) (defn- clean-file-thumbnails! diff --git a/backend/test/app/services_files_test.clj b/backend/test/app/services_files_test.clj index b30e2a53b..aa188b215 100644 --- a/backend/test/app/services_files_test.clj +++ b/backend/test/app/services_files_test.clj @@ -527,7 +527,7 @@ (let [data {::th/type :upsert-file-object-thumbnail :profile-id (:id prof) :file-id (:id file) - :object-id frame1-id + :object-id (str page-id frame1-id) :data "random-data-1"} {:keys [error result] :as out} (th/mutation! data)] @@ -553,7 +553,7 @@ (let [data {::th/type :upsert-file-object-thumbnail :profile-id (:id prof) :file-id (:id file) - :object-id frame1-id + :object-id (str page-id frame1-id) :data nil} {:keys [error result] :as out} (th/mutation! data)] (t/is (nil? error)) @@ -579,7 +579,7 @@ (let [data {::th/type :upsert-file-object-thumbnail :profile-id (:id prof) :file-id (:id file) - :object-id frame1-id + :object-id (str page-id frame1-id) :data "new-data"} {:keys [error result] :as out} (th/mutation! data)] (t/is (nil? error)) @@ -602,7 +602,7 @@ (let [data {::th/type :upsert-file-object-thumbnail :profile-id (:id prof) :file-id (:id file) - :object-id (uuid/next) + :object-id (str page-id (uuid/next)) :data "new-data-2"} {:keys [error result] :as out} (th/mutation! data)] (t/is (nil? error)) diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index a0ed270e4..4c574bde8 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -7,6 +7,7 @@ (ns app.main.data.workspace.thumbnails (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.pages.helpers :as cph] [app.common.uuid :as uuid] [app.main.data.workspace.changes :as dch] @@ -27,57 +28,44 @@ (defn update-thumbnail "Updates the thumbnail information for the given frame `id`" - [id data] - (let [lock (uuid/next)] + [page-id frame-id data] + (let [lock (uuid/next) + object-id (dm/str page-id frame-id)] + (ptk/reify ::update-thumbnail IDeref - (-deref [_] {:id id :data data}) + (-deref [_] {:object-id object-id :data data}) ptk/UpdateEvent (update [_ state] (-> state - (assoc-in [:workspace-file :thumbnails id] data) - (cond-> (nil? (get-in state [::update-thumbnail-lock id])) - (assoc-in [::update-thumbnail-lock id] lock)))) + (assoc-in [:workspace-file :thumbnails object-id] data) + (cond-> (nil? (get-in state [::update-thumbnail-lock object-id])) + (assoc-in [::update-thumbnail-lock object-id] lock)))) ptk/WatchEvent (watch [_ state stream] - (when (= lock (get-in state [::update-thumbnail-lock id])) + (when (= lock (get-in state [::update-thumbnail-lock object-id])) (let [stopper (->> stream (rx/filter (ptk/type? :app.main.data.workspace/finalize))) params {:file-id (:current-file-id state) - :object-id id}] + :object-id object-id}] ;; Sends the first event and debounce the rest. Will only make one update once ;; the 2 second debounce is finished (rx/merge (->> stream (rx/filter (ptk/type? ::update-thumbnail)) (rx/map deref) - (rx/filter #(= id (:id %))) + (rx/filter #(= object-id (:object-id %))) (rx/debounce 2000) (rx/take 1) (rx/map :data) (rx/flat-map #(rp/mutation! :upsert-file-object-thumbnail (assoc params :data %))) - (rx/map #(fn [state] (d/dissoc-in state [::update-thumbnail-lock id]))) + (rx/map #(fn [state] (d/dissoc-in state [::update-thumbnail-lock object-id]))) (rx/take-until stopper)) - (->> (rx/of (update-thumbnail id data)) + (->> (rx/of (update-thumbnail page-id frame-id data)) (rx/observe-on :async))))))))) -(defn remove-thumbnail - [id] - (ptk/reify ::remove-thumbnail - ptk/UpdateEvent - (update [_ state] - (-> state (d/dissoc-in [:workspace-file :thumbnails id]))) - - ptk/WatchEvent - (watch [_ state _] - (let [params {:file-id (:current-file-id state) - :object-id id - :data nil}] - (->> (rp/mutation! :upsert-file-object-thumbnail params) - (rx/ignore)))))) - (defn- extract-frame-changes "Process a changes set in a commit to extract the frames that are changing" [[event [old-objects new-objects]]] diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 961f6af36..839ff694c 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -399,8 +399,11 @@ (l/derived #(dm/get-in % [:workspace-file :thumbnails] {}) st/state)) (defn thumbnail-frame-data - [frame-id] - (l/derived #(get % frame-id) thumbnail-data)) + [page-id frame-id] + (l/derived + (fn [thumbnails] + (get thumbnails (dm/str page-id frame-id))) + thumbnail-data)) (def workspace-text-modifier (l/derived :workspace-text-modifier st/state)) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index e74c3b56a..4102bea86 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.main.data.workspace.thumbnails :as dwt] [app.main.refs :as refs] + [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] [app.main.ui.shapes.embed :as embed] [app.main.ui.shapes.frame :as frame] @@ -67,8 +68,11 @@ ;; Thumbnail data frame-id (:id shape) - thumbnail-data-ref (mf/use-memo (mf/deps frame-id) #(refs/thumbnail-frame-data frame-id)) + page-id (mf/use-ctx ctx/current-page-id) + + thumbnail-data-ref (mf/use-memo (mf/deps page-id frame-id) #(refs/thumbnail-frame-data page-id frame-id)) thumbnail-data (mf/deref thumbnail-data-ref) + thumbnail? (and thumbnail? (or (some? (:thumbnail shape)) (some? thumbnail-data))) ;; References to the current rendered node and the its parentn @@ -84,7 +88,7 @@ disable-thumbnail? (d/not-empty? (dm/get-in modifiers [(:id shape) :modifiers])) [on-load-frame-dom thumb-renderer] - (ftr/use-render-thumbnail shape node-ref rendered? thumbnail? disable-thumbnail?) + (ftr/use-render-thumbnail page-id shape node-ref rendered? thumbnail? disable-thumbnail?) on-frame-load (fns/use-node-store thumbnail? node-ref rendered?)] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/node_store.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/node_store.cljs index f5b73c68f..52c31d920 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/node_store.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/node_store.cljs @@ -28,7 +28,6 @@ (when (and (some? node) (nil? @node-ref)) (let [content (-> (.createElementNS globals/document "http://www.w3.org/2000/svg" "g") (dom/add-class! "frame-content"))] - ;;(.appendChild node content) (reset! node-ref content) (reset! parent-ref node) (swap! re-render inc)))))] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 5aa7489b6..1fb12c750 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -33,7 +33,7 @@ (defn use-render-thumbnail "Hook that will create the thumbnail thata" - [{:keys [id x y width height] :as shape} node-ref rendered? thumbnail? disable?] + [page-id {:keys [id x y width height] :as shape} node-ref rendered? thumbnail? disable?] (let [frame-canvas-ref (mf/use-ref nil) frame-image-ref (mf/use-ref nil) @@ -60,7 +60,7 @@ img-node (mf/ref-val frame-image-ref) thumb-data (draw-thumbnail-canvas canvas-node img-node)] (when (some? thumb-data) - (st/emit! (dw/update-thumbnail id thumb-data)) + (st/emit! (dw/update-thumbnail page-id id thumb-data)) (reset! image-url nil)))))) on-update-frame diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index b27db8869..832577364 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -14,6 +14,7 @@ [app.util.http :as http] [app.worker.impl :as impl] [beicon.core :as rx] + [debug :refer [debug?]] [rumext.alpha :as mf])) (defn- handle-response @@ -115,6 +116,9 @@ (rx/map render-thumbnail) (rx/mapcat persist-thumbnail)))] - (->> (request-thumbnail file-id revn) - (rx/catch not-found? on-cache-miss) - (rx/map on-result)))) + (if (debug? :disable-thumbnail-cachee) + (->> (request-data-for-thumbnail file-id revn) + (rx/map render-thumbnail)) + (->> (request-thumbnail file-id revn) + (rx/catch not-found? on-cache-miss) + (rx/map on-result))))) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index 1cbd600bf..e86e324cc 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -61,6 +61,9 @@ ;; Show text fragments outlines :text-outline + + ;; Disable thumbnail cache + :disable-thumbnail-cachee }) ;; These events are excluded when we activate the :events flag From 3a6072bc8f7b99777a441145dfa1ef63d24f5ba8 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 6 May 2022 13:58:55 +0200 Subject: [PATCH 26/98] :bug: Fix problem with RTL --- frontend/src/app/util/text_position_data.js | 66 ++++++++++++++++++++ frontend/src/app/util/text_svg_position.cljs | 55 +++++----------- 2 files changed, 81 insertions(+), 40 deletions(-) create mode 100644 frontend/src/app/util/text_position_data.js diff --git a/frontend/src/app/util/text_position_data.js b/frontend/src/app/util/text_position_data.js new file mode 100644 index 000000000..cb77fe8eb --- /dev/null +++ b/frontend/src/app/util/text_position_data.js @@ -0,0 +1,66 @@ +/** + * 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 + */ + +"use strict"; + +goog.provide("app.util.text_position_data"); + +goog.scope(function () { + const self = app.util.text_position_data; + const document = goog.global.document; + + function getRangeRects(node, start, end) { + const range = document.createRange(); + range.setStart(node, start); + range.setEnd(node, end); + return range.getClientRects(); + } + + self.parse_text_nodes = function(parent, direction, textNode) { + const content = textNode.textContent; + const textSize = content.length; + const rtl = direction === "rtl"; + + let from = 0; + let to = 0; + let current = ""; + let result = []; + + while (to < textSize) { + const rects = getRangeRects(textNode, from, to + 1); + + if (rects.length > 1) { + let position; + + if (rtl) { + position = rects[1]; + } else { + position = rects[0]; + } + + result.push({ + node: parent, + position: position, + text: current + }); + + from = to; + current = ""; + + } else { + current += content[to]; + to = to + 1; + } + } + + // to == textSize + const rects = getRangeRects(textNode, from, to); + result.push({node: parent, position: rects[0], text: current}); + return result; + }; +}); diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index d88039196..eff905a64 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -11,45 +11,20 @@ [app.common.transit :as transit] [app.main.store :as st] [app.util.dom :as dom] - [app.util.globals :as global])) + [app.util.text-position-data :as tpd])) -(defn get-range-rects - "Retrieve the rectangles that cover the selection given by a `node` adn - the start and end index `start-i`, `end-i`" - [^js node start-i end-i] - (let [^js range (.createRange global/document)] - (.setStart range node start-i) - (.setEnd range node end-i) - (.getClientRects range))) - -;; TODO: Evaluate to change this function to Javascript (defn parse-text-nodes "Given a text node retrieves the rectangles for everyone of its paragraphs and its text." - [parent-node rtl text-node] + [parent-node direction text-node] - (let [content (.-textContent text-node) - text-size (.-length content)] - - (loop [from-i 0 - to-i 0 - current "" - result []] - (if (>= to-i text-size) - (let [rects (get-range-rects text-node from-i to-i) - entry {:node parent-node - :position (dom/bounding-rect->rect (first rects)) - :text current}] - ;; We need to add the last element not closed yet - (conj result entry)) - - (let [rects (get-range-rects text-node from-i (inc to-i))] - ;; If the rects increase means we're in a new paragraph - (if (> (.-length rects) 1) - (let [entry {:node parent-node - :position (dom/bounding-rect->rect (if rtl (second rects) (first rects))) - :text current}] - (recur to-i to-i "" (conj result entry))) - (recur from-i (inc to-i) (str current (nth content to-i)) result))))))) + (letfn [(parse-entry [^js entry] + {:node (.-node entry) + :position (dom/bounding-rect->rect (.-position entry)) + :text (.-text entry)})] + (into + [] + (map parse-entry) + (tpd/parse-text-nodes parent-node direction text-node)))) (defn calc-text-node-positions @@ -87,9 +62,9 @@ (->> text-nodes (mapcat (fn [parent-node] - (let [rtl (= "rtl" (.-dir (.-parentElement parent-node)))] + (let [direction (.-direction (js/getComputedStyle parent-node))] (->> (.-childNodes parent-node) - (mapcat #(parse-text-nodes parent-node rtl %)))))) + (mapcat #(parse-text-nodes parent-node direction %)))))) (mapv #(update % :position translate-rect)))))) (defn calc-position-data @@ -102,15 +77,13 @@ (->> text-data (mapv (fn [{:keys [node position text]}] (let [{:keys [x y width height]} position - rtl (= "rtl" (.-dir (.-parentElement ^js node))) styles (js/getComputedStyle ^js node) get (fn [prop] (let [value (.getPropertyValue styles prop)] (when (and value (not= value "")) value)))] (d/without-nils - {:rtl rtl - :x (if rtl (+ x width) x) + {:x x :y (+ y height) :width width :height height @@ -123,3 +96,5 @@ :font-style (str (get "font-style")) :fills (transit/decode-str (get "--fills")) :text text})))))))))) + + From 6bbd76f350505df338fb7f5b7f4a1f6d58388d91 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 15:16:40 +0200 Subject: [PATCH 27/98] :bug: Fix problem with text shapes in components --- common/src/app/common/pages/common.cljc | 1 + 1 file changed, 1 insertion(+) diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index 5c82be7fc..b7f6f2280 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -25,6 +25,7 @@ :content :content-group :hidden :visibility-group :blocked :modifiable-group + :grow-type :text-font-group :font-family :text-font-group :font-size :text-font-group :font-style :text-font-group From 81dac233a78a1933e6d60e4a2660c846ba622a0b Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 16:08:59 +0200 Subject: [PATCH 28/98] :bug: Fix problem with text edition selection area --- frontend/src/app/main/ui/workspace/viewport.cljs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 715543c84..b17eb4e65 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -297,7 +297,8 @@ (when show-text-editor? [:& text-edition-outline - {:shape (get base-objects edition)}]) + {:shape (get base-objects edition) + :zoom zoom}]) (when show-measures? [:& msr/measurement From 644c796772a329bc09653000ad366d0b3019929c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 16:44:36 +0200 Subject: [PATCH 29/98] :bug: Fix problem with path edition --- .../src/app/main/data/workspace/path/changes.cljs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/frontend/src/app/main/data/workspace/path/changes.cljs b/frontend/src/app/main/data/workspace/path/changes.cljs index 697965eb4..343b33ec2 100644 --- a/frontend/src/app/main/data/workspace/path/changes.cljs +++ b/frontend/src/app/main/data/workspace/path/changes.cljs @@ -23,9 +23,22 @@ (us/verify ::spec/content new-content) (let [shape-id (:id shape) + [old-points old-selrect] + (helpers/content->points+selrect shape old-content) + [new-points new-selrect] (helpers/content->points+selrect shape new-content) + ;; We set the old values so the update-shapes works + objects + (-> objects + (update + shape-id + assoc + :content old-content + :selrect old-selrect + :points old-points)) + changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects))] From 40a38cbd386a8151aa1e31b3ff1d188ebdfa8248 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:01:42 +0200 Subject: [PATCH 30/98] :bug: Fix problem when pasting frame and selected shape --- frontend/src/app/main/data/workspace.cljs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 5a491eaf2..76db0ea07 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -1428,7 +1428,12 @@ wrapper (gsh/selection-rect selected-objs) orig-pos (gpt/point (:x1 wrapper) (:y1 wrapper))] (cond - (and (selected-frame? state) (not has-frame?)) + has-frame? + (let [index (cph/get-position-on-parent page-objects uuid/zero) + delta (gpt/subtract mouse-pos orig-pos)] + [uuid/zero uuid/zero delta index]) + + (selected-frame? state) (let [frame-id (first page-selected) frame-object (get page-objects frame-id) From 94d3f66ef1013a6b7384e01640a7f90ff49031d2 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:30:56 +0200 Subject: [PATCH 31/98] :bug: Fix problem with rotated shapes and auto-width/auto-height --- .../workspace/shapes/text/viewport_texts.cljs | 29 +++++-------------- 1 file changed, 7 insertions(+), 22 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index 7e593d5a6..2045ec671 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -25,6 +25,9 @@ [app.util.timers :as ts] [rumext.alpha :as mf])) +(defn strip-position-data [shape] + (dissoc shape :position-data :transform :transform-inverse)) + (defn- update-with-editor-state "Updates the shape with the current state in the editor" [shape editor-state] @@ -78,26 +81,13 @@ [props] (let [shape (obj/get props "shape") on-update (obj/get props "on-update") - watch-edits (obj/get props "watch-edits") handle-update (mf/use-callback (mf/deps shape on-update) (fn [node] (when (some? node) - (on-update shape node)))) - - text-modifier-ref - (mf/use-memo - (mf/deps (:id shape)) - #(refs/workspace-text-modifier-by-id (:id shape))) - - text-modifier - (when watch-edits (mf/deref text-modifier-ref)) - - shape (cond-> shape - (some? text-modifier) - (dwt/apply-text-modifier text-modifier))] + (on-update shape node))))] [:& fo/text-shape {:key (str "shape-" (:id shape)) :ref handle-update @@ -134,11 +124,6 @@ :on-update handle-update-shape :key (str (dm/str "text-container-" id))}])])) -(defn strip-position-data [[id shape]] - (let [shape (dissoc shape :position-data :transform :transform-inverse)] - [id shape])) - - (mf/defc viewport-text-editing {::mf/wrap-props false} [props] @@ -162,7 +147,6 @@ #(st/emit! (dwt/remove-text-modifier (:id shape))))) [:& text-container {:shape shape - :watch-edits true :on-update handle-update-shape}])) (defn check-props @@ -178,7 +162,8 @@ edition (obj/get props "edition") xf-texts (comp (filter (comp cph/text-shape? second)) - (map strip-position-data)) + (map (fn [[id shape]] + [id (strip-position-data shape)]))) text-shapes (mf/use-memo @@ -198,4 +183,4 @@ [:* (when editing-shape [:& viewport-text-editing {:shape editing-shape}]) - [:& viewport-texts-wrapper {:text-shapes text-shapes}]])) + [:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition)}]])) From 82d744b94a1e9fa5c2afba011cdbbaed0d402382 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 9 May 2022 17:50:34 +0200 Subject: [PATCH 32/98] :bug: Fix problem with scrolling on already visible layers --- frontend/src/app/main/ui/workspace/sidebar/layers.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 62afe4a1f..11c31809c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -189,7 +189,7 @@ (when (and single? selected?) (ts/schedule 100 - #(dom/scroll-into-view! node #js {:block "center", :behavior "smooth"})))] + #(dom/scroll-into-view-if-needed! node #js {:block "center", :behavior "smooth"})))] #(when (some? subid) (rx/dispose! subid))))) From 8d399cb56285585e4d86f32d943f1dff235715fd Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 10 May 2022 08:15:45 +0200 Subject: [PATCH 33/98] :bug: Fix import svg shapes without fill --- frontend/src/app/main/ui/shapes/attrs.cljs | 6 +++-- .../src/app/main/ui/shapes/custom_stroke.cljs | 22 +++++++------------ 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index aeafe56b1..2346da674 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.shapes.attrs (:require + [app.common.colors :as clr] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] @@ -186,11 +187,12 @@ (obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity"))) ;; If contains svg-attrs the origin is svg. If it's not svg origin - ;; we setup the default fill as transparent (instead of black) + ;; we setup the default fill as black (and (contains? shape :svg-attrs) (#{:svg-raw :group} (:type shape)) (empty? (:fills shape))) - styles + (-> styles + (obj/set! "fill" clr/black)) (d/not-empty? (:fills shape)) (add-fill styles (d/without-nils (get-in shape [:fills 0])) render-id 0) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index b124aca04..3f8bb272b 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -6,7 +6,6 @@ (ns app.main.ui.shapes.custom-stroke (:require - [app.common.colors :as clr] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] @@ -361,16 +360,19 @@ (-> props (obj/set! "style" style))) - (and (some? svg-attrs) (obj/contains? svg-attrs "fill")) + (some? svg-attrs) (let [style (-> (obj/get props "style") - (obj/clone) - (obj/set! "fill" (obj/get svg-attrs "fill")) - (obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity")))] + (obj/clone)) + + style (cond-> style + (obj/contains? svg-attrs "fill") + (-> + (obj/set! "fill" (obj/get svg-attrs "fill")) + (obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity"))))] (-> props (obj/set! "style" style))) - (d/not-empty? (:fills shape)) (let [fill-props (attrs/extract-fill-attrs (get-in shape [:fills 0]) render-id 0) @@ -383,14 +385,6 @@ (some? style) (obj/set! "style" style))) - (some? (:svg-attrs shape)) - (let [style - (-> (obj/get props "style") - (obj/clone) - (obj/set! "fill" clr/black))] - (-> props - (obj/set! "style" style))) - (and (= :path (:type shape)) (empty? (:fills shape))) (let [style (-> (obj/get props "style") From 0c26dad3b2a307ef93a386abd5c600af05cacc0f Mon Sep 17 00:00:00 2001 From: Eva Date: Mon, 9 May 2022 16:10:57 +0200 Subject: [PATCH 34/98] :bug: Show selrect in paths --- frontend/src/app/main/ui/workspace/viewport.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 715543c84..f08f7b596 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -168,7 +168,7 @@ show-snap-points? (and (or (contains? layout :dynamic-alignment) (contains? layout :snap-grid)) (or drawing-obj transform)) - show-selrect? (and selrect (empty? drawing) (not edition)) + show-selrect? (and selrect (empty? drawing) (not text-editing?)) show-measures? (and (not transform) (not node-editing?) show-distances?) show-artboard-names? (contains? layout :display-artboard-names) show-rules? (and (contains? layout :rules) (not (contains? layout :hide-ui))) From 8df93c2707b25649ea48894cf8bb97aa8e8ddc98 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 11:50:10 +0200 Subject: [PATCH 35/98] :bug: Fix problem when exporting single text --- frontend/src/app/main/render.cljs | 2 +- .../src/app/main/ui/shapes/text/fontfaces.cljs | 15 +++++++++------ .../src/app/main/ui/workspace/shapes/frame.cljs | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/render.cljs b/frontend/src/app/main/render.cljs index 8e3e77da5..ec193c6af 100644 --- a/frontend/src/app/main/render.cljs +++ b/frontend/src/app/main/render.cljs @@ -402,7 +402,7 @@ :style {:-webkit-print-color-adjust :exact} :fill "none"} - (let [fonts (ff/frame->fonts object objects)] + (let [fonts (ff/shape->fonts object objects)] [:& ff/fontfaces-style {:fonts fonts}]) (case (:type object) diff --git a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs index f2a903948..d08ebbb87 100644 --- a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs +++ b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs @@ -73,12 +73,15 @@ (when (d/not-empty? style) [:style style]))) -(defn frame->fonts - [frame objects] - (->> (cph/get-children objects (:id frame)) - (filter cph/text-shape?) - (map (comp fonts/get-content-fonts :content)) - (reduce set/union #{}))) +(defn shape->fonts + [shape objects] + (let [initial (cond-> #{} + (cph/text-shape? shape) + (into (fonts/get-content-fonts (:content shape))))] + (->> (cph/get-children objects (:id shape)) + (filter cph/text-shape?) + (map (comp fonts/get-content-fonts :content)) + (reduce set/union initial)))) (defn shapes->fonts [shapes] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 4102bea86..254d6a87e 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -61,7 +61,7 @@ thumbnail? (unchecked-get props "thumbnail?") objects (unchecked-get props "objects") - fonts (mf/use-memo (mf/deps shape objects) #(ff/frame->fonts shape objects)) + fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects)) fonts (-> fonts (hooks/use-equal-memo)) force-render (mf/use-state false) From 2976c5c57290b1dce25f309f74c4305a10c74548 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 11:58:44 +0200 Subject: [PATCH 36/98] :bug: Fix problem with flipped texts --- .../src/app/main/ui/shapes/text/svg_text.cljs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index c13f048e6..34e9cf5f3 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -8,6 +8,8 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.config :as cfg] [app.main.ui.context :as muc] @@ -30,6 +32,23 @@ (d/update-when :position-data #(mapv update-color %)) (assoc :stroke-color "#FFFFFF" :stroke-opacity 1)))) +(defn position-data-transform + [shape {:keys [x y width height]}] + (let [rect (gsh/make-rect x (- y height) width height) + center (gsh/center-rect rect)] + (when (or (:flip-x shape) (:flip-y shape)) + (-> (gmt/matrix) + (gmt/translate center) + + (cond-> (:flip-x shape) + (gmt/scale (gpt/point -1 1)) + + (:flip-y shape) + (gmt/scale (gpt/point 1 -1))) + + (gmt/translate (gpt/negate center)) + (dm/str))))) + (mf/defc text-shape {::mf/wrap-props false ::mf/wrap [mf/memo]} @@ -41,7 +60,7 @@ {:keys [x y width height position-data]} shape - transform (str (gsh/transform-matrix shape)) + transform (str (gsh/transform-matrix shape {:no-flip true})) ;; These position attributes are not really necesary but they are convenient for for the export group-props (-> #js {:transform transform @@ -75,6 +94,7 @@ props (-> #js {:key (dm/str "text-" (:id shape) "-" index) :x (:x data) :y y + :transform (position-data-transform shape data) :alignmentBaseline alignment-bl :dominantBaseline dominant-bl :style (-> #js {:fontFamily (:font-family data) From c06042c91b746eddbae39295edc69255b8269f64 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 10 May 2022 13:23:04 +0200 Subject: [PATCH 37/98] :bug: Do not copy the atribute use-for-thumbnail on frame duplicate https://tree.taiga.io/project/penpot/issue/3362 --- frontend/src/app/main/data/workspace.cljs | 5 ++++- frontend/src/app/main/data/workspace/selection.cljs | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 5a491eaf2..2367c08db 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -277,7 +277,10 @@ page (get-in state [:workspace-data :pages-index page-id]) name (dwc/generate-unique-name unames (:name page)) - page (-> page (assoc :name name :id id)) + no_thumbnails_objects (->> (:objects page) + (d/mapm (fn [_ val] (dissoc val :use-for-thumbnail?)))) + + page (-> page (assoc :name name :id id :objects no_thumbnails_objects)) changes (-> (pcb/empty-changes it) (pcb/add-page id page))] diff --git a/frontend/src/app/main/data/workspace/selection.cljs b/frontend/src/app/main/data/workspace/selection.cljs index d4ad40498..5a039e34e 100644 --- a/frontend/src/app/main/data/workspace/selection.cljs +++ b/frontend/src/app/main/data/workspace/selection.cljs @@ -324,6 +324,7 @@ :name frame-name :frame-id uuid/zero :shapes []) + (dissoc :use-for-thumbnail?) (geom/move delta) (d/update-when :interactions #(cti/remap-interactions % ids-map objects))) From 55df28d5dc9514576e4f97e3dbeac4ef50c216d2 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 10 May 2022 10:40:31 +0200 Subject: [PATCH 38/98] :bug: Fix change username if not subscribed to newsletter --- frontend/src/app/main/ui/settings/profile.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/settings/profile.cljs b/frontend/src/app/main/ui/settings/profile.cljs index 2e549ef71..dc86c8121 100644 --- a/frontend/src/app/main/ui/settings/profile.cljs +++ b/frontend/src/app/main/ui/settings/profile.cljs @@ -43,7 +43,10 @@ [] (let [profile (mf/deref refs/profile) initial (mf/with-memo [profile] - (let [subscribed? (-> profile :props :newsletter-subscribed)] + (let [subscribed? (-> profile + :props + :newsletter-subscribed + boolean)] (assoc profile :newsletter-subscribed subscribed?))) form (fm/use-form :spec ::profile-form :initial initial)] From 8432e970cb448f50a5c06cf96fa4e7c913a5f8f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 10 May 2022 15:54:01 +0200 Subject: [PATCH 39/98] :bug: Fix page size at pdf export https://tree.taiga.io/project/penpot/issue/3371 --- frontend/src/app/render.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index 846ac2397..74280552c 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -114,7 +114,7 @@ (rx/map (fn [objects] (let [objects (render/adapt-objects-for-shape objects object-id)] {:objects objects - :object object-id})))))) + :object (get objects object-id)})))))) {:keys [objects object]} (use-resource fetch-state)] From efcabe7ffb49986cb060172deb1c209c8fa8867b Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 11 May 2022 11:03:56 +0200 Subject: [PATCH 40/98] :bug: Fix import svg shapes without fill --- frontend/src/app/main/ui/shapes/attrs.cljs | 2 +- frontend/src/app/main/ui/shapes/shape.cljs | 23 +++++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index 2346da674..d39ba43f2 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -192,7 +192,7 @@ (#{:svg-raw :group} (:type shape)) (empty? (:fills shape))) (-> styles - (obj/set! "fill" clr/black)) + (obj/set! "fill" (or (obj/get (:wrapper-styles shape) "fill") clr/black))) (d/not-empty? (:fills shape)) (add-fill styles (d/without-nils (get-in shape [:fills 0])) render-id 0) diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index 7a24ba008..cfb207beb 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -19,6 +19,21 @@ [app.util.object :as obj] [rumext.alpha :as mf])) +(defn propagate-wrapper-styles + ([children wrapper-props] + (let [children-props-childs (-> (obj/get children "props") + (obj/clone) + (-> (obj/get "childs"))) + + children-props-childs (map #(assoc % :wrapper-styles (obj/get wrapper-props "style")) children-props-childs) + + children-props (-> (obj/get children "props") + (obj/clone) + (obj/set! "childs" children-props-childs))] + + (-> (obj/clone children) + (obj/set! "props" children-props))))) + (mf/defc shape-container {::mf/forward-ref true ::mf/wrap-props false} @@ -56,7 +71,13 @@ wrapper-props (cond-> wrapper-props (= :group type) - (attrs/add-style-attrs shape render-id))] + (attrs/add-style-attrs shape render-id)) + + svg-group? (and (contains? shape :svg-attrs) (= :group type)) + + children (cond-> children + svg-group? + (propagate-wrapper-styles wrapper-props))] [:& (mf/provider muc/render-ctx) {:value render-id} [:> :g wrapper-props From 33e776fefe3fe508caa93feb77dbbd584907da09 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 12:38:29 +0200 Subject: [PATCH 41/98] :bug: Fix path handler radius --- .../main/ui/workspace/shapes/path/editor.cljs | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs index 1b7842454..cee647cde 100644 --- a/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/path/editor.cljs @@ -25,6 +25,17 @@ [rumext.alpha :as mf]) (:import goog.events.EventType)) +(def point-radius 5) +(def point-radius-selected 4) +(def point-radius-active-area 15) +(def point-radius-stroke-width 1) + +(def handler-side 6) +(def handler-stroke-width 1) + +(def path-preview-dasharray 4) +(def path-snap-stroke-width 1) + (mf/defc path-point [{:keys [position zoom edit-mode hover? selected? preview? start-path? last-p? new-point? curve?]}] (let [{:keys [x y]} position @@ -72,8 +83,8 @@ [:circle.path-point {:cx x :cy y - :r (if (or selected? hover?) (/ 3.5 zoom) (/ 3 zoom)) - :style {:stroke-width (/ 1 zoom) + :r (if (or selected? hover?) (/ point-radius zoom) (/ point-radius-selected zoom)) + :style {:stroke-width (/ point-radius-stroke-width zoom) :stroke (cond (or selected? hover?) pc/black-color preview? pc/secondary-color :else pc/primary-color) @@ -81,7 +92,7 @@ :else pc/white-color)}}] [:circle {:cx x :cy y - :r (/ 10 zoom) + :r (/ point-radius-active-area zoom) :on-mouse-down on-mouse-down :on-mouse-enter on-enter :on-mouse-leave on-leave @@ -119,7 +130,7 @@ :x2 x :y2 y :style {:stroke (if hover? pc/black-color pc/gray-color) - :stroke-width (/ 1 zoom)}}] + :stroke-width (/ point-radius-stroke-width zoom)}}] (when snap-angle? [:line @@ -128,22 +139,22 @@ :x2 x :y2 y :style {:stroke pc/secondary-color - :stroke-width (/ 1 zoom)}}]) + :stroke-width (/ point-radius-stroke-width zoom)}}]) [:rect - {:x (- x (/ 3 zoom)) - :y (- y (/ 3 zoom)) - :width (/ 6 zoom) - :height (/ 6 zoom) + {:x (- x (/ handler-side 2 zoom)) + :y (- y (/ handler-side 2 zoom)) + :width (/ handler-side zoom) + :height (/ handler-side zoom) - :style {:stroke-width (/ 1 zoom) + :style {:stroke-width (/ handler-stroke-width zoom) :stroke (cond (or selected? hover?) pc/black-color :else pc/primary-color) :fill (cond selected? pc/primary-color :else pc/white-color)}}] [:circle {:cx x :cy y - :r (/ 10 zoom) + :r (/ point-radius-active-area zoom) :on-mouse-down on-mouse-down :on-mouse-enter on-enter :on-mouse-leave on-leave @@ -156,8 +167,8 @@ (when (not= :move-to (:command command)) [:path {:style {:fill "none" :stroke pc/black-color - :stroke-width (/ 1 zoom) - :stroke-dasharray (/ 4 zoom)} + :stroke-width (/ handler-stroke-width zoom) + :stroke-dasharray (/ path-preview-dasharray zoom)} :d (upf/format-path [{:command :move-to :params {:x (:x from) :y (:y from)}} @@ -178,7 +189,7 @@ :x2 (:x to) :y2 (:y to) :style {:stroke pc/secondary-color - :stroke-width (/ 1 zoom)}}])])) + :stroke-width (/ path-snap-stroke-width zoom)}}])])) (defn matching-handler? [content node handlers] (when (= 2 (count handlers)) From 40de8781ef4a4aec40925ba4246b58e2b59fe3c6 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 15:43:28 +0200 Subject: [PATCH 42/98] :bug: Improved zoom responsiveness --- .../main/ui/workspace/viewport/actions.cljs | 36 ++-- frontend/src/app/util/dom/normalize_wheel.js | 181 ++++++++++++++++++ 2 files changed, 196 insertions(+), 21 deletions(-) create mode 100644 frontend/src/app/util/dom/normalize_wheel.js diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 7fbfeb487..32aaf6e0c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -21,12 +21,14 @@ [app.util.dom :as dom] [app.util.dom.dnd :as dnd] [app.util.keyboard :as kbd] + [app.util.normalize-wheel :as nw] [app.util.object :as obj] [app.util.timers :as timers] [beicon.core :as rx] [cuerdas.core :as str] - [rumext.alpha :as mf]) - (:import goog.events.WheelEvent)) + [rumext.alpha :as mf])) + +(def scale-per-pixel -0.0057) (defn on-mouse-down [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? @@ -377,32 +379,24 @@ (dom/stop-propagation event) (let [pt (->> (dom/get-client-position event) (utils/translate-point-to-viewport viewport zoom)) + + norm-event ^js (nw/normalize-wheel event) ctrl? (kbd/ctrl? event) + delta-y (.-pixelY norm-event) + delta-x (.-pixelX norm-event)] - delta-mode (.-deltaMode ^js event) - - unit (cond - (= delta-mode WheelEvent.DeltaMode.PIXEL) 1 - (= delta-mode WheelEvent.DeltaMode.LINE) 16 - (= delta-mode WheelEvent.DeltaMode.PAGE) 100) - - delta-y (-> (.-deltaY ^js event) - (* unit) - (/ zoom)) - - delta-x (-> (.-deltaX ^js event) - (* unit) - (/ zoom))] (if (or ctrl? mod?) - (let [delta (* -1 (+ delta-y delta-x)) - scale (-> (+ 1 (/ delta 100)) (mth/clamp 0.77 1.3))] + (let [delta-zoom (+ delta-y delta-x) + scale (+ 1 (mth/abs (* scale-per-pixel delta-zoom))) + scale (if (pos? delta-zoom) (/ 1 scale) scale)] (st/emit! (dw/set-zoom pt scale))) + (if (and (not (cfg/check-platform? :macos)) ;; macos sends delta-x automatically, don't need to do it (kbd/shift? event)) - (st/emit! (dw/update-viewport-position {:x #(+ % delta-y)})) - (st/emit! (dw/update-viewport-position {:x #(+ % delta-x) - :y #(+ % delta-y)})))))))))) + (st/emit! (dw/update-viewport-position {:x #(+ % (/ delta-y zoom))})) + (st/emit! (dw/update-viewport-position {:x #(+ % (/ delta-x zoom)) + :y #(+ % (/ delta-y zoom))})))))))))) (defn on-drag-enter [] (mf/use-callback diff --git a/frontend/src/app/util/dom/normalize_wheel.js b/frontend/src/app/util/dom/normalize_wheel.js new file mode 100644 index 000000000..c9f22399f --- /dev/null +++ b/frontend/src/app/util/dom/normalize_wheel.js @@ -0,0 +1,181 @@ +/** + * Copyright (c) 2015, Facebook, Inc. + * All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + * + * @providesModule normalizeWheel + * @typechecks + */ + +/** + Adapted to google closure from: + https://raw.githubusercontent.com/facebookarchive/fixed-data-table/master/src/vendor_upstream/dom/normalizeWheel.js +*/ + +'use strict'; + +goog.provide("app.util.normalize_wheel"); + +goog.scope(function() { + const self = app.util.normalize_wheel; + + // const UserAgent_DEPRECATED = require('UserAgent_DEPRECATED'); + + // const isEventSupported = require('isEventSupported'); + + + // Reasonable defaults + const PIXEL_STEP = 10; + const LINE_HEIGHT = 40; + const PAGE_HEIGHT = 800; + + /** + * Mouse wheel (and 2-finger trackpad) support on the web sucks. It is + * complicated, thus this doc is long and (hopefully) detailed enough to answer + * your questions. + * + * If you need to react to the mouse wheel in a predictable way, this code is + * like your bestest friend. * hugs * + * + * As of today, there are 4 DOM event types you can listen to: + * + * 'wheel' -- Chrome(31+), FF(17+), IE(9+) + * 'mousewheel' -- Chrome, IE(6+), Opera, Safari + * 'MozMousePixelScroll' -- FF(3.5 only!) (2010-2013) -- don't bother! + * 'DOMMouseScroll' -- FF(0.9.7+) since 2003 + * + * So what to do? The is the best: + * + * normalizeWheel.getEventType(); + * + * In your event callback, use this code to get sane interpretation of the + * deltas. This code will return an object with properties: + * + * spinX -- normalized spin speed (use for zoom) - x plane + * spinY -- " - y plane + * pixelX -- normalized distance (to pixels) - x plane + * pixelY -- " - y plane + * + * Wheel values are provided by the browser assuming you are using the wheel to + * scroll a web page by a number of lines or pixels (or pages). Values can vary + * significantly on different platforms and browsers, forgetting that you can + * scroll at different speeds. Some devices (like trackpads) emit more events + * at smaller increments with fine granularity, and some emit massive jumps with + * linear speed or acceleration. + * + * This code does its best to normalize the deltas for you: + * + * - spin is trying to normalize how far the wheel was spun (or trackpad + * dragged). This is super useful for zoom support where you want to + * throw away the chunky scroll steps on the PC and make those equal to + * the slow and smooth tiny steps on the Mac. Key data: This code tries to + * resolve a single slow step on a wheel to 1. + * + * - pixel is normalizing the desired scroll delta in pixel units. You'll + * get the crazy differences between browsers, but at least it'll be in + * pixels! + * + * - positive value indicates scrolling DOWN/RIGHT, negative UP/LEFT. This + * should translate to positive value zooming IN, negative zooming OUT. + * This matches the newer 'wheel' event. + * + * Why are there spinX, spinY (or pixels)? + * + * - spinX is a 2-finger side drag on the trackpad, and a shift + wheel turn + * with a mouse. It results in side-scrolling in the browser by default. + * + * - spinY is what you expect -- it's the classic axis of a mouse wheel. + * + * - I dropped spinZ/pixelZ. It is supported by the DOM 3 'wheel' event and + * probably is by browsers in conjunction with fancy 3D controllers .. but + * you know. + * + * Implementation info: + * + * Examples of 'wheel' event if you scroll slowly (down) by one step with an + * average mouse: + * + * OS X + Chrome (mouse) - 4 pixel delta (wheelDelta -120) + * OS X + Safari (mouse) - N/A pixel delta (wheelDelta -12) + * OS X + Firefox (mouse) - 0.1 line delta (wheelDelta N/A) + * Win8 + Chrome (mouse) - 100 pixel delta (wheelDelta -120) + * Win8 + Firefox (mouse) - 3 line delta (wheelDelta -120) + * + * On the trackpad: + * + * OS X + Chrome (trackpad) - 2 pixel delta (wheelDelta -6) + * OS X + Firefox (trackpad) - 1 pixel delta (wheelDelta N/A) + * + * On other/older browsers.. it's more complicated as there can be multiple and + * also missing delta values. + * + * The 'wheel' event is more standard: + * + * http://www.w3.org/TR/DOM-Level-3-Events/#events-wheelevents + * + * The basics is that it includes a unit, deltaMode (pixels, lines, pages), and + * deltaX, deltaY and deltaZ. Some browsers provide other values to maintain + * backward compatibility with older events. Those other values help us + * better normalize spin speed. Example of what the browsers provide: + * + * | event.wheelDelta | event.detail + * ------------------+------------------+-------------- + * Safari v5/OS X | -120 | 0 + * Safari v5/Win7 | -120 | 0 + * Chrome v17/OS X | -120 | 0 + * Chrome v17/Win7 | -120 | 0 + * IE9/Win7 | -120 | undefined + * Firefox v4/OS X | undefined | 1 + * Firefox v4/Win7 | undefined | 3 + * + */ + function normalizeWheel(/*object*/ event) /*object*/ { + var sX = 0, sY = 0, // spinX, spinY + pX = 0, pY = 0; // pixelX, pixelY + + // Legacy + if ('detail' in event) { sY = event.detail; } + if ('wheelDelta' in event) { sY = -event.wheelDelta / 120; } + if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120; } + if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120; } + + // side scrolling on FF with DOMMouseScroll + if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) { + sX = sY; + sY = 0; + } + + pX = sX * PIXEL_STEP; + pY = sY * PIXEL_STEP; + + if ('deltaY' in event) { pY = event.deltaY; } + if ('deltaX' in event) { pX = event.deltaX; } + + if ((pX || pY) && event.deltaMode) { + if (event.deltaMode == 1) { // delta in LINE units + pX *= LINE_HEIGHT; + pY *= LINE_HEIGHT; + } else { // delta in PAGE units + pX *= PAGE_HEIGHT; + pY *= PAGE_HEIGHT; + } + } + + // Fall-back if spin cannot be determined + if (pX && !sX) { sX = (pX < 1) ? -1 : 1; } + if (pY && !sY) { sY = (pY < 1) ? -1 : 1; } + + return { spinX : sX, + spinY : sY, + pixelX : pX, + pixelY : pY }; + } + + + self.normalize_wheel = normalizeWheel; + +}); + From af6a6871870f4b0eb6686e406417757adb26f7d2 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 10 May 2022 17:00:22 +0200 Subject: [PATCH 43/98] :bug: Fix performance problem with import SVG --- frontend/src/app/main/data/workspace/svg_upload.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 6e564b1d3..9f8147e11 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -414,7 +414,6 @@ new-shape (dwc/make-new-shape shape objects selected) changes (-> changes - (pcb/with-objects objects) (pcb/add-object new-shape) (pcb/change-parent parent-id [new-shape] index)) @@ -467,6 +466,7 @@ ;; Creates the root shape new-shape (dwc/make-new-shape root-shape objects selected) changes (-> (pcb/empty-changes it page-id) + (pcb/with-objects objects) (pcb/add-object new-shape)) root-attrs (-> (:attrs svg-data) From f2bb59fd77255cd865158db94c8d71091a2d770e Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 11 May 2022 13:05:52 +0200 Subject: [PATCH 44/98] :bug: Fix Paths have a black fill while being drawn --- frontend/src/app/main/ui/workspace/viewport.cljs | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 370e3806a..cb45c78b3 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -257,6 +257,7 @@ :ref viewport-ref :class (when drawing-tool "drawing") :style {:cursor @cursor} + :fill "none" :on-click on-click :on-context-menu on-context-menu From c14ece9f8d82fa13236a05cc09f342376702397a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 11 May 2022 13:44:47 +0200 Subject: [PATCH 45/98] :bug: Fix problems with thumbnails --- .../app/main/data/workspace/thumbnails.cljs | 5 +- frontend/src/app/main/ui/shapes/frame.cljs | 43 ++-- .../app/main/ui/workspace/shapes/frame.cljs | 19 +- .../shapes/frame/dynamic_modifiers.cljs | 221 +++++++++++++++++- .../src/app/main/ui/workspace/viewport.cljs | 2 +- .../app/main/ui/workspace/viewport/hooks.cljs | 17 +- .../app/main/ui/workspace/viewport/utils.cljs | 214 ----------------- 7 files changed, 271 insertions(+), 250 deletions(-) diff --git a/frontend/src/app/main/data/workspace/thumbnails.cljs b/frontend/src/app/main/data/workspace/thumbnails.cljs index 4c574bde8..8a5c117ff 100644 --- a/frontend/src/app/main/data/workspace/thumbnails.cljs +++ b/frontend/src/app/main/data/workspace/thumbnails.cljs @@ -153,7 +153,8 @@ (ptk/reify ::duplicate-thumbnail ptk/UpdateEvent (update [_ state] - (let [old-shape-thumbnail (get-in state [:workspace-file :thumbnails old-id])] - (-> state (assoc-in [:workspace-file :thumbnails new-id] old-shape-thumbnail)))))) + (let [page-id (get state :current-page-id) + old-shape-thumbnail (get-in state [:workspace-file :thumbnails (dm/str page-id old-id)])] + (-> state (assoc-in [:workspace-file :thumbnails (dm/str page-id new-id)] old-shape-thumbnail)))))) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 91425cd79..168e26893 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -55,17 +55,26 @@ :width width :height height :className "frame-background"})) - path? (some? (.-d props))] + path? (some? (.-d props)) + render-id (mf/use-ctx muc/render-ctx)] + [:* - [:image.frame-thumbnail - {:id (dm/str "thumbnail-" (:id shape)) - :xlinkHref (:thumbnail shape) - :x (:x shape) - :y (:y shape) - :width (:width shape) - :height (:height shape) - ;; DEBUG - :style {:filter (when (debug? :thumbnails) "sepia(1)")}}] + [:g {:clip-path (frame-clip-url shape render-id)} + [:& frame-clip-def {:shape shape :render-id render-id}] + [:& shape-fills {:shape shape} + (if path? + [:> :path props] + [:> :rect props])] + + [:image.frame-thumbnail + {:id (dm/str "thumbnail-" (:id shape)) + :xlinkHref (:thumbnail shape) + :x (:x shape) + :y (:y shape) + :width (:width shape) + :height (:height shape) + ;; DEBUG + :style {:filter (when (debug? :thumbnails) "sepia(1)")}}]] [:& shape-strokes {:shape shape} (if path? @@ -96,16 +105,16 @@ [:* [:g {:clip-path (frame-clip-url shape render-id)} - [:* - [:& shape-fills {:shape shape} - (if path? - [:> :path props] - [:> :rect props])] + [:& shape-fills {:shape shape} + (if path? + [:> :path props] + [:> :rect props])] + [:g.frame-children (for [item childs] [:& shape-wrapper {:shape item - :key (dm/str (:id item))}]) - ]] + :key (dm/str (:id item))}])]] + [:& shape-strokes {:shape shape} (if path? [:> :path props] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 254d6a87e..a2b71dee7 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.uuid :as uuid] [app.main.data.workspace.thumbnails :as dwt] [app.main.refs :as refs] [app.main.ui.context :as ctx] @@ -61,6 +62,7 @@ thumbnail? (unchecked-get props "thumbnail?") objects (unchecked-get props "objects") + render-id (mf/use-memo #(str (uuid/next))) fonts (mf/use-memo (mf/deps shape objects) #(ff/shape->fonts shape objects)) fonts (-> fonts (hooks/use-equal-memo)) @@ -73,7 +75,7 @@ thumbnail-data-ref (mf/use-memo (mf/deps page-id frame-id) #(refs/thumbnail-frame-data page-id frame-id)) thumbnail-data (mf/deref thumbnail-data-ref) - thumbnail? (and thumbnail? (or (some? (:thumbnail shape)) (some? thumbnail-data))) + thumbnail? (and thumbnail? (some? thumbnail-data)) ;; References to the current rendered node and the its parentn node-ref (mf/use-var nil) @@ -117,11 +119,12 @@ @node-ref) (when (not @rendered?) (reset! rendered? true))))) - [:g.frame-container {:key "frame-container" :ref on-frame-load} - [:g.frame-thumbnail {:id (dm/str "thumbnail-container-" (:id shape))} - [:> frame/frame-thumbnail {:key (dm/str (:id shape)) - :shape (cond-> shape - (some? thumbnail-data) - (assoc :thumbnail thumbnail-data))}] + [:& (mf/provider ctx/render-ctx) {:value render-id} + [:g.frame-container {:key "frame-container" :ref on-frame-load} + [:g.frame-thumbnail-wrapper {:id (dm/str "thumbnail-container-" (:id shape))} + [:> frame/frame-thumbnail {:key (dm/str (:id shape)) + :shape (cond-> shape + (some? thumbnail-data) + (assoc :thumbnail thumbnail-data))}] - thumb-renderer]])))) + thumb-renderer]]])))) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 3624c041e..9ec66d7d6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -7,10 +7,223 @@ (ns app.main.ui.workspace.shapes.frame.dynamic-modifiers (:require [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.geom.matrix :as gmt] + [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] - [app.main.ui.workspace.viewport.utils :as utils] + [app.util.dom :as dom] [rumext.alpha :as mf])) +(defn- transform-no-resize + "If we apply a scale directly to the texts it will show deformed so we need to create this + correction matrix to \"undo\" the resize but keep the other transformations." + [{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers] + + (let [corner-pt (first points) + corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse)) + + resize-x? (some? (:resize-vector modifiers)) + resize-y? (some? (:resize-vector-2 modifiers)) + + flip-x? (neg? (get-in modifiers [:resize-vector :x])) + flip-y? (or (neg? (get-in modifiers [:resize-vector :y])) + (neg? (get-in modifiers [:resize-vector-2 :y]))) + + result (cond-> (gmt/matrix) + (and (some? transform) (or resize-x? resize-y?)) + (gmt/multiply transform) + + resize-x? + (gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt) + + resize-y? + (gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt) + + flip-x? + (gmt/scale (gpt/point -1 1) corner-pt) + + flip-y? + (gmt/scale (gpt/point 1 -1) corner-pt) + + (and (some? transform) (or resize-x? resize-y?)) + (gmt/multiply transform-inverse)) + + [width height] + (if (or resize-x? resize-y?) + (let [pc (cond-> (gpt/point x y) + (some? transform) + (gpt/transform transform) + + (some? current-transform) + (gpt/transform current-transform)) + + pw (cond-> (gpt/point (+ x width) y) + (some? transform) + (gpt/transform transform) + + (some? current-transform) + (gpt/transform current-transform)) + + ph (cond-> (gpt/point x (+ y height)) + (some? transform) + (gpt/transform transform) + + (some? current-transform) + (gpt/transform current-transform))] + [(gpt/distance pc pw) (gpt/distance pc ph)]) + [width height])] + + [result width height])) + +(defn get-nodes + "Retrieve the DOM nodes to apply the matrix transformation" + [base-node {:keys [id type masked-group?]}] + (let [shape-node (dom/query base-node (str "#shape-" id)) + + frame? (= :frame type) + group? (= :group type) + text? (= :text type) + mask? (and group? masked-group?)] + + (cond + frame? + [shape-node + (dom/query shape-node ".frame-children") + (dom/query (str "#thumbnail-container-" id)) + (dom/query (str "#thumbnail-" id))] + + ;; For groups we don't want to transform the whole group but only + ;; its filters/masks + mask? + [(dom/query shape-node ".mask-clip-path") + (dom/query shape-node ".mask-shape")] + + group? + (let [shape-defs (dom/query shape-node "defs")] + (d/concat-vec + (dom/query-all shape-defs ".svg-def") + (dom/query-all shape-defs ".svg-mask-wrapper"))) + + text? + [shape-node + (dom/query shape-node ".text-shape")] + + :else + [shape-node]))) + +(defn transform-region! + [node modifiers] + + (let [{:keys [x y width height]} + (-> (gsh/make-selrect + (-> (dom/get-attribute node "data-old-x") d/parse-double) + (-> (dom/get-attribute node "data-old-y") d/parse-double) + (-> (dom/get-attribute node "data-old-width") d/parse-double) + (-> (dom/get-attribute node "data-old-height") d/parse-double)) + (gsh/transform-selrect modifiers))] + (dom/set-attribute! node "x" x) + (dom/set-attribute! node "y" y) + (dom/set-attribute! node "width" width) + (dom/set-attribute! node "height" height))) + +(defn start-transform! + [base-node shapes] + (doseq [shape shapes] + (when-let [nodes (get-nodes base-node shape)] + (doseq [node nodes] + (let [old-transform (dom/get-attribute node "transform")] + (when (some? old-transform) + (dom/set-attribute! node "data-old-transform" old-transform)) + + (when (or (= (dom/get-tag-name node) "linearGradient") + (= (dom/get-tag-name node) "radialGradient")) + (let [gradient-transform (dom/get-attribute node "gradientTransform")] + (when (some? gradient-transform) + (dom/set-attribute! node "data-old-gradientTransform" gradient-transform)))) + + (when (= (dom/get-tag-name node) "pattern") + (let [pattern-transform (dom/get-attribute node "patternTransform")] + (when (some? pattern-transform) + (dom/set-attribute! node "data-old-patternTransform" pattern-transform)))) + + (when (or (= (dom/get-tag-name node) "mask") + (= (dom/get-tag-name node) "filter")) + (let [old-x (dom/get-attribute node "x") + old-y (dom/get-attribute node "y") + old-width (dom/get-attribute node "width") + old-height (dom/get-attribute node "height")] + (dom/set-attribute! node "data-old-x" old-x) + (dom/set-attribute! node "data-old-y" old-y) + (dom/set-attribute! node "data-old-width" old-width) + (dom/set-attribute! node "data-old-height" old-height)))))))) + +(defn set-transform-att! + [node att value] + + (let [old-att (dom/get-attribute node (dm/str "data-old-" att)) + new-value (if (some? old-att) + (dm/str value " " old-att) + (str value))] + (dom/set-attribute! node att (str new-value)))) + +(defn update-transform! + [base-node shapes transforms modifiers] + (doseq [{:keys [id] :as shape} shapes] + (when-let [nodes (get-nodes base-node shape)] + (let [transform (get transforms id) + modifiers (get-in modifiers [id :modifiers])] + + (doseq [node nodes] + (cond + ;; Text shapes need special treatment because their resize only change + ;; the text area, not the change size/position + (or (dom/class? node "text-shape") + (dom/class? node "frame-thumbnail")) + (let [[transform] (transform-no-resize shape transform modifiers)] + (set-transform-att! node "transform" transform)) + + (dom/class? node "frame-children") + (set-transform-att! node "transform" (gmt/inverse transform)) + + (or (= (dom/get-tag-name node) "mask") + (= (dom/get-tag-name node) "filter")) + (transform-region! node modifiers) + + (or (= (dom/get-tag-name node) "linearGradient") + (= (dom/get-tag-name node) "radialGradient")) + (set-transform-att! node "gradientTransform" transform) + + (= (dom/get-tag-name node) "pattern") + (set-transform-att! node "patternTransform" transform) + + (and (some? transform) (some? node)) + (set-transform-att! node "transform" transform))))))) + +(defn remove-transform! + [base-node shapes] + (doseq [shape shapes] + (when-let [nodes (get-nodes base-node shape)] + (doseq [node nodes] + (when (some? node) + (cond + (= (dom/get-tag-name node) "foreignObject") + ;; The shape width/height will be automaticaly setup when the modifiers are applied + nil + + (or (= (dom/get-tag-name node) "mask") + (= (dom/get-tag-name node) "filter")) + (do + (dom/remove-attribute! node "data-old-x") + (dom/remove-attribute! node "data-old-y") + (dom/remove-attribute! node "data-old-width") + (dom/remove-attribute! node "data-old-height")) + + :else + (let [old-transform (dom/get-attribute node "data-old-transform")] + (if (some? old-transform) + (dom/remove-attribute! node "data-old-transform") + (dom/remove-attribute! node "transform"))))))))) + (defn use-dynamic-modifiers [objects node modifiers] @@ -42,13 +255,13 @@ is-cur-val? (d/not-empty? modifiers)] (when (and (not is-prev-val?) is-cur-val?) - (utils/start-transform! node shapes)) + (start-transform! node shapes)) (when is-cur-val? - (utils/update-transform! node shapes transforms modifiers)) + (update-transform! node shapes transforms modifiers)) (when (and is-prev-val? (not is-cur-val?)) - (utils/remove-transform! node @prev-shapes)) + (remove-transform! node @prev-shapes)) (reset! prev-modifiers modifiers) (reset! prev-transforms transforms) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index cb45c78b3..3abcde449 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -183,7 +183,7 @@ (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom) (hooks/setup-viewport-modifiers modifiers base-objects) (hooks/setup-shortcuts node-editing? drawing-path?) - (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom) + (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform) [:div.viewport [:div.viewport-overlays {:ref overlays-ref} diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 9f656d1de..8cd4fda9c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -219,20 +219,29 @@ (gsh/overlaps? frame vbox)))) (defn setup-active-frames - [objects hover-ids selected active-frames zoom] + [objects hover-ids selected active-frames zoom transform] (mf/use-effect - (mf/deps objects @hover-ids selected zoom) + (mf/deps objects @hover-ids selected zoom transform) (fn [] (when (some? @hover-ids) (let [hover-frame (when (> zoom 0.25) (last @hover-ids)) new-active-frames (if (some? hover-frame) #{hover-frame} #{}) + + frame? #(= :frame (get-in objects [% :type])) + + selected-frames (->> selected (filter frame?)) + new-active-frames + (cond-> new-active-frames + (and (not= transform :move) (= (count selected-frames) 1)) + (conj new-active-frames (first selected-frames))) + new-active-frames (into new-active-frames (comp - (filter #(not= :frame (get-in objects [% :type]))) + (remove frame?) (map #(get-in objects [% :frame-id]))) - selected) ] + selected)] (reset! active-frames new-active-frames)))))) ;; NOTE: this is executed on each page change, maybe we need to move diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index fe5c4586e..7f29d4776 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -8,224 +8,10 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] - [app.common.geom.shapes :as gsh] [app.main.ui.cursors :as cur] [app.util.dom :as dom])) -(defn- text-corrected-transform - "If we apply a scale directly to the texts it will show deformed so we need to create this - correction matrix to \"undo\" the resize but keep the other transformations." - [{:keys [x y width height points transform transform-inverse] :as shape} current-transform modifiers] - - (let [corner-pt (first points) - corner-pt (cond-> corner-pt (some? transform-inverse) (gpt/transform transform-inverse)) - - resize-x? (some? (:resize-vector modifiers)) - resize-y? (some? (:resize-vector-2 modifiers)) - - flip-x? (neg? (get-in modifiers [:resize-vector :x])) - flip-y? (or (neg? (get-in modifiers [:resize-vector :y])) - (neg? (get-in modifiers [:resize-vector-2 :y]))) - - result (cond-> (gmt/matrix) - (and (some? transform) (or resize-x? resize-y?)) - (gmt/multiply transform) - - resize-x? - (gmt/scale (gpt/inverse (:resize-vector modifiers)) corner-pt) - - resize-y? - (gmt/scale (gpt/inverse (:resize-vector-2 modifiers)) corner-pt) - - flip-x? - (gmt/scale (gpt/point -1 1) corner-pt) - - flip-y? - (gmt/scale (gpt/point 1 -1) corner-pt) - - (and (some? transform) (or resize-x? resize-y?)) - (gmt/multiply transform-inverse)) - - [width height] - (if (or resize-x? resize-y?) - (let [pc (cond-> (gpt/point x y) - (some? transform) - (gpt/transform transform) - - (some? current-transform) - (gpt/transform current-transform)) - - pw (cond-> (gpt/point (+ x width) y) - (some? transform) - (gpt/transform transform) - - (some? current-transform) - (gpt/transform current-transform)) - - ph (cond-> (gpt/point x (+ y height)) - (some? transform) - (gpt/transform transform) - - (some? current-transform) - (gpt/transform current-transform))] - [(gpt/distance pc pw) (gpt/distance pc ph)]) - [width height])] - - [result width height])) - -(defn get-nodes - "Retrieve the DOM nodes to apply the matrix transformation" - [base-node {:keys [id type masked-group?]}] - (let [shape-node (dom/query base-node (str "#shape-" id)) - - frame? (= :frame type) - group? (= :group type) - text? (= :text type) - mask? (and group? masked-group?) - - ;; When the shape is a frame we maybe need to move its thumbnail - thumb-node (when frame? (dom/query (str "#thumbnail-container-" id)))] - - (cond - frame? - [thumb-node - (dom/get-parent (dom/query shape-node ".frame-background")) - (dom/query shape-node ".frame-clip")] - - ;; For groups we don't want to transform the whole group but only - ;; its filters/masks - mask? - [(dom/query shape-node ".mask-clip-path") - (dom/query shape-node ".mask-shape")] - - group? - (let [shape-defs (dom/query shape-node "defs")] - (d/concat-vec - (dom/query-all shape-defs ".svg-def") - (dom/query-all shape-defs ".svg-mask-wrapper"))) - - text? - [shape-node - (dom/query shape-node ".text-shape")] - - :else - [shape-node]))) - -(defn transform-region! - [node modifiers] - - (let [{:keys [x y width height]} - (-> (gsh/make-selrect - (-> (dom/get-attribute node "data-old-x") d/parse-double) - (-> (dom/get-attribute node "data-old-y") d/parse-double) - (-> (dom/get-attribute node "data-old-width") d/parse-double) - (-> (dom/get-attribute node "data-old-height") d/parse-double)) - (gsh/transform-selrect modifiers))] - (dom/set-attribute! node "x" x) - (dom/set-attribute! node "y" y) - (dom/set-attribute! node "width" width) - (dom/set-attribute! node "height" height))) - -(defn start-transform! - [base-node shapes] - (doseq [shape shapes] - (when-let [nodes (get-nodes base-node shape)] - (doseq [node nodes] - (let [old-transform (dom/get-attribute node "transform")] - (when (some? old-transform) - (dom/set-attribute! node "data-old-transform" old-transform)) - - (when (or (= (dom/get-tag-name node) "linearGradient") - (= (dom/get-tag-name node) "radialGradient")) - (let [gradient-transform (dom/get-attribute node "gradientTransform")] - (when (some? gradient-transform) - (dom/set-attribute! node "data-old-gradientTransform" gradient-transform)))) - - (when (= (dom/get-tag-name node) "pattern") - (let [pattern-transform (dom/get-attribute node "patternTransform")] - (when (some? pattern-transform) - (dom/set-attribute! node "data-old-patternTransform" pattern-transform)))) - - (when (or (= (dom/get-tag-name node) "mask") - (= (dom/get-tag-name node) "filter")) - (let [old-x (dom/get-attribute node "x") - old-y (dom/get-attribute node "y") - old-width (dom/get-attribute node "width") - old-height (dom/get-attribute node "height")] - (dom/set-attribute! node "data-old-x" old-x) - (dom/set-attribute! node "data-old-y" old-y) - (dom/set-attribute! node "data-old-width" old-width) - (dom/set-attribute! node "data-old-height" old-height)))))))) - -(defn set-transform-att! - [node att value] - - (let [old-att (dom/get-attribute node (dm/str "data-old-" att)) - new-value (if (some? old-att) - (dm/str value " " old-att) - (str value))] - (dom/set-attribute! node att (str new-value)))) - -(defn update-transform! - [base-node shapes transforms modifiers] - (doseq [{:keys [id type] :as shape} shapes] - (when-let [nodes (get-nodes base-node shape)] - (let [transform (get transforms id) - modifiers (get-in modifiers [id :modifiers]) - - [text-transform _text-width _text-height] - (when (= :text type) - (text-corrected-transform shape transform modifiers))] - - (doseq [node nodes] - (cond - ;; Text shapes need special treatment because their resize only change - ;; the text area, not the change size/position - (dom/class? node "text-shape") - (when (some? text-transform) - (set-transform-att! node "transform" text-transform)) - - (or (= (dom/get-tag-name node) "mask") - (= (dom/get-tag-name node) "filter")) - (transform-region! node modifiers) - - (or (= (dom/get-tag-name node) "linearGradient") - (= (dom/get-tag-name node) "radialGradient")) - (set-transform-att! node "gradientTransform" transform) - - (= (dom/get-tag-name node) "pattern") - (set-transform-att! node "patternTransform" transform) - - (and (some? transform) (some? node)) - (set-transform-att! node "transform" transform))))))) - -(defn remove-transform! - [base-node shapes] - (doseq [shape shapes] - (when-let [nodes (get-nodes base-node shape)] - (doseq [node nodes] - (when (some? node) - (cond - (= (dom/get-tag-name node) "foreignObject") - ;; The shape width/height will be automaticaly setup when the modifiers are applied - nil - - (or (= (dom/get-tag-name node) "mask") - (= (dom/get-tag-name node) "filter")) - (do - (dom/remove-attribute! node "data-old-x") - (dom/remove-attribute! node "data-old-y") - (dom/remove-attribute! node "data-old-width") - (dom/remove-attribute! node "data-old-height")) - - :else - (let [old-transform (dom/get-attribute node "data-old-transform")] - (if (some? old-transform) - (dom/remove-attribute! node "data-old-transform") - (dom/remove-attribute! node "transform"))))))))) - (defn format-viewbox [vbox] (dm/str (:x vbox 0) " " (:y vbox 0) " " From ce41a38098a289d1195f31239e5e6a378abc8025 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Wed, 11 May 2022 13:41:55 +0200 Subject: [PATCH 46/98] :tada: Show an error page when the user uses a cancelled/invalid/expired invitation --- .../src/app/rpc/mutations/verify_token.clj | 16 ++++++++++++--- .../src/app/main/ui/auth/verify_token.cljs | 20 +++++++++++-------- frontend/translations/ar.po | 3 --- frontend/translations/ca.po | 4 ---- frontend/translations/de.po | 4 ---- frontend/translations/en.po | 8 ++++++-- frontend/translations/es.po | 8 ++++++-- frontend/translations/fr.po | 4 ---- frontend/translations/he.po | 4 ---- frontend/translations/pt_BR.po | 4 ---- frontend/translations/ro.po | 4 ---- frontend/translations/ru.po | 4 ---- frontend/translations/tr.po | 4 ---- frontend/translations/zh_CN.po | 4 ---- frontend/translations/zh_Hant.po | 4 ---- 15 files changed, 37 insertions(+), 58 deletions(-) diff --git a/backend/src/app/rpc/mutations/verify_token.clj b/backend/src/app/rpc/mutations/verify_token.clj index f6bd0b290..dbae70834 100644 --- a/backend/src/app/rpc/mutations/verify_token.clj +++ b/backend/src/app/rpc/mutations/verify_token.clj @@ -12,7 +12,7 @@ [app.loggers.audit :as audit] [app.rpc.mutations.teams :as teams] [app.rpc.queries.profile :as profile] - [app.util.services :as sv] + [app.util.services :as sv] [clojure.spec.alpha :as s] [cuerdas.core :as str])) @@ -114,15 +114,25 @@ {:is-active true} {:id member-id})) (assoc member :is-active true) - + ;; Delete the invitation (db/delete! conn :team-invitation {:team-id team-id :email-to (str/lower member-email)}))) - + (defmethod process-token :team-invitation [cfg {:keys [profile-id token]} {:keys [member-id] :as claims}] (us/assert ::team-invitation-claims claims) + #_(let [conn (:conn cfg) + team-id (:team-id claims) + member-email (:member-email claims) + invitation (db/get-by-params conn :team-invitation + {:team-id team-id :email-to (str/lower member-email)} + {:check-not-found false})] + (when (nil? invitation) + (ex/raise :type :validation + :code :invalid-token))) + (cond ;; This happens when token is filled with member-id and current ;; user is already logged in with exactly invited account. diff --git a/frontend/src/app/main/ui/auth/verify_token.cljs b/frontend/src/app/main/ui/auth/verify_token.cljs index 1f6a7c9aa..20cf4efcc 100644 --- a/frontend/src/app/main/ui/auth/verify_token.cljs +++ b/frontend/src/app/main/ui/auth/verify_token.cljs @@ -11,6 +11,7 @@ [app.main.repo :as rp] [app.main.store :as st] [app.main.ui.icons :as i] + [app.main.ui.static :as static] [app.util.dom :as dom] [app.util.i18n :as i18n :refer [tr]] [app.util.router :as rt] @@ -59,7 +60,8 @@ (mf/defc verify-token [{:keys [route] :as props}] - (let [token (get-in route [:query-params :token])] + (let [token (get-in route [:query-params :token]) + bad-token (mf/use-state false)] (mf/use-effect (fn [] (dom/set-html-title (tr "title.default")) @@ -69,13 +71,10 @@ (handle-token tdata)) (fn [{:keys [type code] :as error}] (cond - (and (= :validation type) + (or (= :validation type) (= :invalid-token code) (= :token-expired (:reason error))) - (let [msg (tr "errors.token-expired")] - (ts/schedule 100 #(st/emit! (dm/error msg))) - (st/emit! (rt/nav :auth-login))) - + (reset! bad-token true) (= :email-already-exists code) (let [msg (tr "errors.email-already-exists")] (ts/schedule 100 #(st/emit! (dm/error msg))) @@ -91,5 +90,10 @@ (ts/schedule 100 #(st/emit! (dm/error msg))) (st/emit! (rt/nav :auth-login))))))))) - [:div.verify-token - i/loader-pencil])) + (if @bad-token + [:> static/static-header {} + [:div.image i/unchain] + [:div.main-message (tr "errors.invite-invalid")] + [:div.desc-message (tr "errors.invite-invalid.info")]] + [:div.verify-token + i/loader-pencil]))) diff --git a/frontend/translations/ar.po b/frontend/translations/ar.po index c1af242ae..970b2686a 100644 --- a/frontend/translations/ar.po +++ b/frontend/translations/ar.po @@ -539,9 +539,6 @@ msgstr "التسجيل معطل حاليا." msgid "errors.terms-privacy-agreement-invalid" msgstr "يجب أن تقبل شروط الخدمة وسياسة الخصوصية الخاصة بنا." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "انتهت صلاحية الرمز" #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" diff --git a/frontend/translations/ca.po b/frontend/translations/ca.po index e35e4e4fd..22c98e847 100644 --- a/frontend/translations/ca.po +++ b/frontend/translations/ca.po @@ -701,10 +701,6 @@ msgstr "" "Heu d'acceptar les nostres condicions del servei i la política de " "privacitat." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "El codi ha caducat" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "S'ha produït un error inesperat." diff --git a/frontend/translations/de.po b/frontend/translations/de.po index 4d9310d0f..56ad00622 100644 --- a/frontend/translations/de.po +++ b/frontend/translations/de.po @@ -700,10 +700,6 @@ msgstr "" "Sie müssen unsere Nutzungsbedingungen und Datenschutzrichtlinien " "akzeptieren." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Token abgelaufen" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Ein unerwarteter Fehler ist aufgetreten." diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 46081d3e3..9b8d65519 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -729,8 +729,12 @@ msgid "errors.terms-privacy-agreement-invalid" msgstr "You must accept our terms of service and privacy policy." #: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Token expired" +msgid "errors.invite-invalid" +msgstr "Invite invalid" + +msgid "errors.invite-invalid.info" +msgstr "This invite might be canceled or may be expired." + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 0aa4de77c..20ff6a5bc 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -730,8 +730,12 @@ msgid "errors.terms-privacy-agreement-invalid" msgstr "Debes aceptar nuestros términos de servicio y política de privacidad." #: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Token expirado" +msgid "errors.invite-invalid" +msgstr "Invitación inválida" + +msgid "errors.invite-invalid.info" +msgstr "Esta invitación puede haber sido cancelada o ha expirado." + #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" diff --git a/frontend/translations/fr.po b/frontend/translations/fr.po index 88c306d11..69642842d 100644 --- a/frontend/translations/fr.po +++ b/frontend/translations/fr.po @@ -596,10 +596,6 @@ msgstr "" "Vous devez accepter nos conditions générales d'utilisation et notre " "politique de confidentialité." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Jeton expiré" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Une erreur inattendue s’est produite" diff --git a/frontend/translations/he.po b/frontend/translations/he.po index 81c531816..56a228872 100644 --- a/frontend/translations/he.po +++ b/frontend/translations/he.po @@ -673,10 +673,6 @@ msgstr "הבעלים לא יכולים לעזוב את הקבוצה, עליך ל msgid "errors.terms-privacy-agreement-invalid" msgstr "עליך לקבל את תנאי השירות ואת מדיניות הפרטיות." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "תוקף האסימון פג" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "אירעה שגיאה בלתי צפויה." diff --git a/frontend/translations/pt_BR.po b/frontend/translations/pt_BR.po index 1e5824f4a..b029f171d 100644 --- a/frontend/translations/pt_BR.po +++ b/frontend/translations/pt_BR.po @@ -518,10 +518,6 @@ msgstr "O registro de contas está desativado no momento." msgid "errors.terms-privacy-agreement-invalid" msgstr "Você deve aceitar nossos termos de serviço e política de privacidade." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Token expirado" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Um erro inesperado ocorreu." diff --git a/frontend/translations/ro.po b/frontend/translations/ro.po index 14f72441b..d517ef5f7 100644 --- a/frontend/translations/ro.po +++ b/frontend/translations/ro.po @@ -509,10 +509,6 @@ msgstr "Înregistrarea este dezactivată în prezent." msgid "errors.terms-privacy-agreement-invalid" msgstr "Trebuie să acceptați termenii serviciului și politica de confidențialitate." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Codul este expirat" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "A apărut o eroare neașteptată." diff --git a/frontend/translations/ru.po b/frontend/translations/ru.po index 6435831d1..223b15139 100644 --- a/frontend/translations/ru.po +++ b/frontend/translations/ru.po @@ -692,10 +692,6 @@ msgstr "Нужно переназначить роль владельца пер msgid "errors.terms-privacy-agreement-invalid" msgstr "Вы должны принять наши правила использования и политику конфиденциальности." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Токен истёк" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Произошла ошибка." diff --git a/frontend/translations/tr.po b/frontend/translations/tr.po index 8d635e19b..5c3549f68 100644 --- a/frontend/translations/tr.po +++ b/frontend/translations/tr.po @@ -692,10 +692,6 @@ msgstr "Sahip takımdan ayrılamaz, sahip rolünü yeniden atamanız gerekir." msgid "errors.terms-privacy-agreement-invalid" msgstr "Hizmet şartlarımızı ve gizlilik politikamızı kabul etmelisin." -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "Jetonun süresi geçti" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "Beklenmedik bir hata oluştu." diff --git a/frontend/translations/zh_CN.po b/frontend/translations/zh_CN.po index e12aed3cd..fad641b40 100644 --- a/frontend/translations/zh_CN.po +++ b/frontend/translations/zh_CN.po @@ -644,10 +644,6 @@ msgstr "当前禁止注册。" msgid "errors.terms-privacy-agreement-invalid" msgstr "你必须接受我们的使用条例和隐私政策。" -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "令牌已过期" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "发生了意料之外的错误。" diff --git a/frontend/translations/zh_Hant.po b/frontend/translations/zh_Hant.po index 9627b07e1..1c09f5ae2 100644 --- a/frontend/translations/zh_Hant.po +++ b/frontend/translations/zh_Hant.po @@ -583,10 +583,6 @@ msgstr "目前並不開放註冊。" msgid "errors.terms-privacy-agreement-invalid" msgstr "您必須接受我們的服務條款和隱私權政策。" -#: src/app/main/ui/auth/verify_token.cljs -msgid "errors.token-expired" -msgstr "權杖過期" - #: src/app/main/data/media.cljs, src/app/main/ui/workspace/sidebar/options/menus/exports.cljs, src/app/main/ui/handoff/exports.cljs msgid "errors.unexpected-error" msgstr "發生了預料之外的錯誤。" From fef69cb707417901eaf2d5848f239c19c34b240a Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 11 May 2022 15:53:27 +0200 Subject: [PATCH 47/98] :bug: Fix problem with RTL texts --- frontend/src/app/main/ui/shapes/text/svg_text.cljs | 6 ++++-- frontend/src/app/util/text_position_data.js | 13 ++++--------- frontend/src/app/util/text_svg_position.cljs | 12 +++++++----- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 34e9cf5f3..24894825c 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -91,8 +91,9 @@ alignment-bl (when (cfg/check-browser? :safari) "text-before-edge") dominant-bl (when-not (cfg/check-browser? :safari) "ideographic") + rtl? (= "rtl"(:direction data)) props (-> #js {:key (dm/str "text-" (:id shape) "-" index) - :x (:x data) + :x (if rtl? (+ (:x data) (:width data)) (:x data)) :y y :transform (position-data-transform shape data) :alignmentBaseline alignment-bl @@ -104,7 +105,7 @@ :textDecoration (:text-decoration data) :letterSpacing (:letter-spacing data) :fontStyle (:font-style data) - :direction (if (:rtl data) "rtl" "ltr") + :direction (:direction data) :whiteSpace "pre"} (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) shape (assoc shape :fills (:fills data))] @@ -112,3 +113,4 @@ [:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} [:& shape-custom-strokes {:shape shape :key index} [:> :text props (:text data)]]]))]])) + diff --git a/frontend/src/app/util/text_position_data.js b/frontend/src/app/util/text_position_data.js index cb77fe8eb..40a18b3ba 100644 --- a/frontend/src/app/util/text_position_data.js +++ b/frontend/src/app/util/text_position_data.js @@ -21,27 +21,21 @@ goog.scope(function () { return range.getClientRects(); } - self.parse_text_nodes = function(parent, direction, textNode) { + self.parse_text_nodes = function(parent, textNode) { const content = textNode.textContent; const textSize = content.length; - const rtl = direction === "rtl"; let from = 0; let to = 0; let current = ""; let result = []; + let prevRect = null; while (to < textSize) { const rects = getRangeRects(textNode, from, to + 1); if (rects.length > 1) { - let position; - - if (rtl) { - position = rects[1]; - } else { - position = rects[0]; - } + const position = prevRect; result.push({ node: parent, @@ -53,6 +47,7 @@ goog.scope(function () { current = ""; } else { + prevRect = rects[0]; current += content[to]; to = to + 1; } diff --git a/frontend/src/app/util/text_svg_position.cljs b/frontend/src/app/util/text_svg_position.cljs index eff905a64..e25a70f6f 100644 --- a/frontend/src/app/util/text_svg_position.cljs +++ b/frontend/src/app/util/text_svg_position.cljs @@ -18,13 +18,14 @@ [parent-node direction text-node] (letfn [(parse-entry [^js entry] - {:node (.-node entry) - :position (dom/bounding-rect->rect (.-position entry)) - :text (.-text entry)})] + {:node (.-node entry) + :position (dom/bounding-rect->rect (.-position entry)) + :text (.-text entry) + :direction direction})] (into [] (map parse-entry) - (tpd/parse-text-nodes parent-node direction text-node)))) + (tpd/parse-text-nodes parent-node text-node)))) (defn calc-text-node-positions @@ -75,7 +76,7 @@ (let [text-data (calc-text-node-positions base-node viewport zoom)] (when (d/not-empty? text-data) (->> text-data - (mapv (fn [{:keys [node position text]}] + (mapv (fn [{:keys [node position text direction]}] (let [{:keys [x y width height]} position styles (js/getComputedStyle ^js node) get (fn [prop] @@ -87,6 +88,7 @@ :y (+ y height) :width width :height height + :direction direction :font-family (str (get "font-family")) :font-size (str (get "font-size")) :font-weight (str (get "font-weight")) From b9e984300c027997004117e6209f787aaee8ac8f Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 11 May 2022 12:20:17 +0200 Subject: [PATCH 48/98] :bug: Avoid scroll behind fixed element in layers --- .../src/app/main/ui/workspace/sidebar/layers.cljs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 11c31809c..b76e9b7d8 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -96,7 +96,8 @@ container? (or (cph/frame-shape? item) (cph/group-shape? item)) - disable-drag (mf/use-state false) + disable-drag (mf/use-state false) + scroll-to-middle? (mf/use-var true) expanded-iref (mf/use-memo (mf/deps id) @@ -129,6 +130,7 @@ select-shape (fn [event] (dom/prevent-default event) + (reset! scroll-to-middle? false) (let [id (:id item)] (cond (kbd/shift? event) @@ -187,9 +189,14 @@ subid (when (and single? selected?) - (ts/schedule - 100 - #(dom/scroll-into-view-if-needed! node #js {:block "center", :behavior "smooth"})))] + (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))))) From 767f0fe16b08a359d234be3b53145820c988cddf Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 11 May 2022 12:47:21 +0200 Subject: [PATCH 49/98] :bug: Fix multiple fills with texts are not working properly --- .../src/app/main/ui/shapes/custom_stroke.cljs | 21 +++++++++++-------- .../src/app/main/ui/shapes/text/svg_text.cljs | 2 +- 2 files changed, 13 insertions(+), 10 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 3f8bb272b..b125f8d45 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -318,7 +318,7 @@ [:& stroke-defs {:shape shape :render-id render-id :index index}]] child]))) -(defn build-fill-props [shape child render-id] +(defn build-fill-props [shape child position render-id] (let [url-fill? (or (some? (:fill-image shape)) (= :image (:type shape)) (> (count (:fills shape)) 1) @@ -349,7 +349,7 @@ (-> (obj/get props "style") (obj/clone) (obj/without ["fill" "fillOpacity"])))] - (obj/set! props "fill" (dm/fmt "url(#fill-0-%)" render-id))) + (obj/set! props "fill" (dm/fmt "url(#fill-%-%)" position render-id))) (and (some? svg-styles) (obj/contains? svg-styles "fill")) (let [style @@ -415,9 +415,10 @@ (let [child (obj/get props "children") shape (obj/get props "shape") elem-name (obj/get child "type") - render-id (mf/use-ctx muc/render-ctx)] + position (or (obj/get props "position") 0) + render-id (or (obj/get props "render-id") (mf/use-ctx muc/render-ctx))] [:g {:id (dm/fmt "fills-%" (:id shape))} - [:> elem-name (build-fill-props shape child render-id)]])) + [:> elem-name (build-fill-props shape child position render-id)]])) (mf/defc shape-strokes {::mf/wrap-props false} @@ -425,7 +426,7 @@ (let [child (obj/get props "children") shape (obj/get props "shape") elem-name (obj/get child "type") - render-id (mf/use-ctx muc/render-ctx) + render-id (or (obj/get props "render-id") (mf/use-ctx muc/render-ctx)) stroke-id (dm/fmt "strokes-%" (:id shape)) stroke-props (-> (obj/new) (obj/set! "id" stroke-id) @@ -450,8 +451,10 @@ (mf/defc shape-custom-strokes {::mf/wrap-props false} [props] - (let [children (obj/get props "children") - shape (obj/get props "shape")] + (let [children (obj/get props "children") + shape (obj/get props "shape") + position (obj/get props "position") + render-id (obj/get props "render-id")] [:* - [:& shape-fills {:shape shape} children] - [:& shape-strokes {:shape shape} children]])) + [:& shape-fills {:shape shape :position position :render-id render-id} children] + [:& shape-strokes {:shape shape :position position :render-id render-id} children]])) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 34e9cf5f3..7a1c68154 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -110,5 +110,5 @@ shape (assoc shape :fills (:fills data))] [:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} - [:& shape-custom-strokes {:shape shape :key index} + [:& shape-custom-strokes {:shape shape :position index :render-id render-id} [:> :text props (:text data)]]]))]])) From b7b5f3b4c2d9074d5537788c5d7bc6a831dfef4d Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 12 May 2022 14:18:26 +0200 Subject: [PATCH 50/98] Fix Scrollbars not shown --- frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs index 9aba15377..2f46fd96c 100644 --- a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.viewport.scroll-bars (:require + [app.common.colors :as clr] [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] [app.main.data.workspace :as dw] @@ -188,7 +189,7 @@ [:* (when show-v-scroll? - [:g.v-scroll + [:g.v-scroll {:fill clr/black} [:rect {:on-mouse-move #(on-mouse-move % :y) :on-mouse-down #(on-mouse-down % :y) :on-mouse-up on-mouse-up @@ -202,7 +203,7 @@ :style {:stroke "white" :stroke-width (/ 0.15 zoom)}}]]) (when show-h-scroll? - [:g.h-scroll + [:g.h-scroll {:fill clr/black} [:rect {:on-mouse-move #(on-mouse-move % :x) :on-mouse-down #(on-mouse-down % :x) :on-mouse-up on-mouse-up From 8b1f8d1418461ef2f6e1344e8ce1f24523c6d817 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 12 May 2022 15:18:23 +0200 Subject: [PATCH 51/98] :bug: Fix error in view mode --- frontend/src/app/main/ui/shapes/shape.cljs | 34 ++++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index cfb207beb..790776004 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -19,20 +19,30 @@ [app.util.object :as obj] [rumext.alpha :as mf])) +(defn propagate-wrapper-styles-child + [child wrapper-props] + (let [child-props-childs + (-> (obj/get child "props") + (obj/clone) + (-> (obj/get "childs"))) + + child-props-childs + (->> child-props-childs + (map #(assoc % :wrapper-styles (obj/get wrapper-props "style")))) + + child-props + (-> (obj/get child "props") + (obj/clone) + (obj/set! "childs" child-props-childs))] + + (-> (obj/clone child) + (obj/set! "props" child-props)))) + (defn propagate-wrapper-styles ([children wrapper-props] - (let [children-props-childs (-> (obj/get children "props") - (obj/clone) - (-> (obj/get "childs"))) - - children-props-childs (map #(assoc % :wrapper-styles (obj/get wrapper-props "style")) children-props-childs) - - children-props (-> (obj/get children "props") - (obj/clone) - (obj/set! "childs" children-props-childs))] - - (-> (obj/clone children) - (obj/set! "props" children-props))))) + (if (.isArray js/Array children) + (->> children (map propagate-wrapper-styles-child)) + (-> children (propagate-wrapper-styles-child wrapper-props))))) (mf/defc shape-container {::mf/forward-ref true From 7446fe77b3c4872868a480f273d6a09215818ea6 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 11 May 2022 22:13:29 +0200 Subject: [PATCH 52/98] :bug: Change text disposition on resize --- .../app/common/geom/shapes/constraints.cljc | 26 +++++++++++- .../src/app/main/ui/shapes/text/styles.cljs | 2 +- .../src/app/main/ui/shapes/text/svg_text.cljs | 13 +++--- .../shapes/frame/dynamic_modifiers.cljs | 16 ++++---- .../app/main/ui/workspace/shapes/text.cljs | 38 ++++++++--------- .../workspace/shapes/text/viewport_texts.cljs | 41 +++++++++++++------ .../src/app/main/ui/workspace/viewport.cljs | 1 + 7 files changed, 90 insertions(+), 47 deletions(-) diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 5c295acef..32064b22d 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -152,6 +152,28 @@ :top :scale))) +(defn clean-modifiers + "Remove redundant modifiers" + [{:keys [displacement resize-vector resize-vector-2] :as modifiers}] + + (cond-> modifiers + ;; Displacement with value 0. We don't move in any direction + (and (some? displacement) + (mth/almost-zero? (:e displacement)) + (mth/almost-zero? (:f displacement))) + (dissoc :displacement) + + ;; Resize with value very close to 1 means no resize + (and (some? resize-vector) + (mth/almost-zero? (- 1.0 (:x resize-vector))) + (mth/almost-zero? (- 1.0 (:y resize-vector)))) + (dissoc :resize-origin :resize-vector) + + (and (some? resize-vector) + (mth/almost-zero? (- 1.0 (:x resize-vector-2))) + (mth/almost-zero? (- 1.0 (:y resize-vector-2)))) + (dissoc :resize-origin-2 :resize-vector-2))) + (defn calc-child-modifiers [parent child modifiers ignore-constraints transformed-parent-rect] (let [constraints-h @@ -192,5 +214,7 @@ (:resize-transform modifiers) (assoc :resize-transform (:resize-transform modifiers) - :resize-transform-inverse (:resize-transform-inverse modifiers))))) + :resize-transform-inverse (:resize-transform-inverse modifiers)) + :always + (clean-modifiers)))) diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index cd2a21c5b..f6490e5fb 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -50,7 +50,7 @@ text-align (:text-align data "start") base #js {:fontSize (str (:font-size data (:font-size txt/default-text-attrs)) "px") :lineHeight (:line-height data (:line-height txt/default-text-attrs)) - :margin "inherit"}] + :margin 0}] (cond-> base (some? line-height) (obj/set! "lineHeight" line-height) (some? text-align) (obj/set! "textAlign" text-align)))) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index d46025e4d..79d1127f1 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -78,10 +78,11 @@ [:defs (for [[index data] (d/enumerate position-data)] (when (some? (:fill-color-gradient data)) - [:& grad/gradient {:id (str "fill-color-gradient_" (get-gradient-id index)) - :key index - :attr :fill-color-gradient - :shape data}]))]) + (let [id (dm/str "fill-color-gradient_" (get-gradient-id index))] + [:& grad/gradient {:id id + :key id + :attr :fill-color-gradient + :shape data}])))]) [:> :g group-props (for [[index data] (d/enumerate position-data)] @@ -91,7 +92,7 @@ alignment-bl (when (cfg/check-browser? :safari) "text-before-edge") dominant-bl (when-not (cfg/check-browser? :safari) "ideographic") - rtl? (= "rtl"(:direction data)) + rtl? (= "rtl" (:direction data)) props (-> #js {:key (dm/str "text-" (:id shape) "-" index) :x (if rtl? (+ (:x data) (:width data)) (:x data)) :y y @@ -110,7 +111,7 @@ (obj/set! "fill" (str "url(#fill-" index "-" render-id ")")))}) shape (assoc shape :fills (:fills data))] - [:& (mf/provider muc/render-ctx) {:value (str render-id "_" (:id shape) "_" index)} + [:& (mf/provider muc/render-ctx) {:key index :value (str render-id "_" (:id shape) "_" index)} [:& shape-custom-strokes {:shape shape :position index :render-id render-id} [:> :text props (:text data)]]]))]])) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 9ec66d7d6..e465205c7 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -82,7 +82,6 @@ frame? (= :frame type) group? (= :group type) - text? (= :text type) mask? (and group? masked-group?)] (cond @@ -104,10 +103,6 @@ (dom/query-all shape-defs ".svg-def") (dom/query-all shape-defs ".svg-mask-wrapper"))) - text? - [shape-node - (dom/query shape-node ".text-shape")] - :else [shape-node]))) @@ -177,8 +172,7 @@ (cond ;; Text shapes need special treatment because their resize only change ;; the text area, not the change size/position - (or (dom/class? node "text-shape") - (dom/class? node "frame-thumbnail")) + (dom/class? node "frame-thumbnail") (let [[transform] (transform-no-resize shape transform modifiers)] (set-transform-att! node "transform" transform)) @@ -233,7 +227,13 @@ (fn [] (when (some? modifiers) (d/mapm (fn [id {modifiers :modifiers}] - (let [center (gsh/center-shape (get objects id))] + (let [shape (get objects id) + center (gsh/center-shape shape) + modifiers (cond-> modifiers + ;; For texts we only use the displacement because + ;; resize needs to recalculate the text layout + (= :text (:type shape)) + (select-keys [:displacement :rotation]))] (gsh/modifiers->transform center modifiers))) modifiers)))) diff --git a/frontend/src/app/main/ui/workspace/shapes/text.cljs b/frontend/src/app/main/ui/workspace/shapes/text.cljs index 821fca536..7a5b662d9 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.workspace.shapes.text (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.math :as mth] [app.main.data.workspace.texts :as dwt] [app.main.refs :as refs] @@ -32,24 +33,23 @@ (dwt/apply-text-modifier text-modifier))] [:> shape-container {:shape shape} - [:* - [:g.text-shape - [:& text/text-shape {:shape shape}]] + [:g.text-shape {:key (dm/str "text-" (:id shape))} + [:& text/text-shape {:shape shape}]] - (when (and (debug? :text-outline) (d/not-empty? (:position-data shape))) - (for [data (:position-data shape)] - (let [{:keys [x y width height]} data] - [:* - ;; Text fragment bounding box - [:rect {:x x - :y (- y height) - :width width - :height height - :style {:fill "none" :stroke "red"}}] + (when (and (debug? :text-outline) (d/not-empty? (:position-data shape))) + (for [[index data] (d/enumerate (:position-data shape))] + (let [{:keys [x y width height]} data] + [:g {:key (dm/str index)} + ;; Text fragment bounding box + [:rect {:x x + :y (- y height) + :width width + :height height + :style {:fill "none" :stroke "red"}}] - ;; Text baselineazo - [:line {:x1 (mth/round x) - :y1 (mth/round (- (:y data) (:height data))) - :x2 (mth/round (+ x width)) - :y2 (mth/round (- (:y data) (:height data))) - :style {:stroke "blue"}}]])))]])) + ;; Text baselineazo + [:line {:x1 (mth/round x) + :y1 (mth/round (- (:y data) (:height data))) + :x2 (mth/round (+ x width)) + :y2 (mth/round (- (:y data) (:height data))) + :style {:stroke "blue"}}]])))])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index 2045ec671..fea2ad1d9 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -9,6 +9,7 @@ [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.geom.shapes :as gsh] [app.common.math :as mth] [app.common.pages.helpers :as cph] [app.common.text :as txt] @@ -28,6 +29,15 @@ (defn strip-position-data [shape] (dissoc shape :position-data :transform :transform-inverse)) +(defn process-shape [modifiers {:keys [id] :as shape}] + (let [modifier (get modifiers id) + modifier (d/update-when modifier :modifiers dissoc :displacement :rotation) + shape (cond-> shape + (not (gsh/empty-modifiers? modifier)) + (-> (assoc :grow-type :fixed) + (merge modifier) gsh/transform-shape))] + (strip-position-data shape))) + (defn- update-with-editor-state "Updates the shape with the current state in the editor" [shape editor-state] @@ -88,7 +98,6 @@ (fn [node] (when (some? node) (on-update shape node))))] - [:& fo/text-shape {:key (str "shape-" (:id shape)) :ref handle-update :shape shape @@ -120,7 +129,7 @@ [:* (for [{:keys [id] :as shape} changed-texts] - [:& text-container {:shape shape + [:& text-container {:shape (gsh/transform-shape shape) :on-update handle-update-shape :key (str (dm/str "text-container-" id))}])])) @@ -151,24 +160,30 @@ (defn check-props [new-props old-props] - (and (identical? (unchecked-get new-props "objects") (unchecked-get old-props "objects")) - (= (unchecked-get new-props "edition") (unchecked-get old-props "edition")))) + (and (identical? (unchecked-get new-props "objects") + (unchecked-get old-props "objects")) + (identical? (unchecked-get new-props "modifiers") + (unchecked-get old-props "modifiers")) + (= (unchecked-get new-props "edition") + (unchecked-get old-props "edition")))) (mf/defc viewport-texts {::mf/wrap-props false ::mf/wrap [#(mf/memo' % check-props)]} [props] - (let [objects (obj/get props "objects") - edition (obj/get props "edition") - - xf-texts (comp (filter (comp cph/text-shape? second)) - (map (fn [[id shape]] - [id (strip-position-data shape)]))) + (let [objects (obj/get props "objects") + edition (obj/get props "edition") + modifiers (obj/get props "modifiers") text-shapes (mf/use-memo (mf/deps objects) - #(into {} xf-texts objects)) + #(into {} (filter (comp cph/text-shape? second)) objects)) + + text-shapes + (mf/use-memo + (mf/deps text-shapes modifiers) + #(d/update-vals text-shapes (partial process-shape modifiers))) editing-shape (get text-shapes edition)] @@ -183,4 +198,6 @@ [:* (when editing-shape [:& viewport-text-editing {:shape editing-shape}]) - [:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition)}]])) + + [:& viewport-texts-wrapper {:text-shapes (dissoc text-shapes edition) + :modifiers modifiers}]])) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 3abcde449..0224855ac 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -246,6 +246,7 @@ [:& stv/viewport-texts {:key (dm/str "texts-" page-id) :page-id page-id :objects base-objects + :modifiers modifiers :edition edition}]]] [:svg.viewport-controls From d85a4d653994e23f846acf829b6fd44105162ed4 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 12 May 2022 12:54:52 +0200 Subject: [PATCH 53/98] :bug: Minor improvements on refs --- common/src/app/common/geom/matrix.cljc | 10 ++++ .../main/data/workspace/state_helpers.cljs | 31 ++++++++++- frontend/src/app/main/refs.cljs | 51 +++++++------------ frontend/src/app/main/ui/shapes/filters.cljs | 6 ++- .../src/app/main/ui/shapes/text/svg_text.cljs | 1 + .../shapes/frame/dynamic_modifiers.cljs | 20 ++++++++ .../main/ui/workspace/viewport/widgets.cljs | 3 +- 7 files changed, 84 insertions(+), 38 deletions(-) diff --git a/common/src/app/common/geom/matrix.cljc b/common/src/app/common/geom/matrix.cljc index b8e0bf19e..b0b200997 100644 --- a/common/src/app/common/geom/matrix.cljc +++ b/common/src/app/common/geom/matrix.cljc @@ -219,3 +219,13 @@ e' (/ (- (* c f) (* d e)) det) f' (/ (- (* b e) (* a f)) det)] (Matrix. a' b' c' d' e' f'))) + +(defn round + [mtx] + (-> mtx + (update :a mth/precision 4) + (update :b mth/precision 4) + (update :c mth/precision 4) + (update :d mth/precision 4) + (update :e mth/precision 4) + (update :f mth/precision 4))) diff --git a/frontend/src/app/main/data/workspace/state_helpers.cljs b/frontend/src/app/main/data/workspace/state_helpers.cljs index 4da32f002..9b4986922 100644 --- a/frontend/src/app/main/data/workspace/state_helpers.cljs +++ b/frontend/src/app/main/data/workspace/state_helpers.cljs @@ -8,7 +8,9 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.pages.helpers :as cph])) + [app.common.geom.shapes :as gsh] + [app.common.pages.helpers :as cph] + [app.common.path.commands :as upc])) (defn lookup-page ([state] @@ -50,6 +52,10 @@ (filter selectable?) selected))))) +(defn lookup-selected-raw + [state] + (dm/get-in state [:workspace-local :selected])) + (defn lookup-selected ([state] (lookup-selected state nil)) @@ -94,3 +100,26 @@ (-> (:workspace-libraries state) (assoc id {:id id :data local})))) + +(defn- set-content-modifiers [state] + (fn [id shape] + (let [content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers])] + (if (some? content-modifiers) + (update shape :content upc/apply-content-modifiers content-modifiers) + shape)))) + +(defn select-bool-children + [parent-id state] + (let [objects (lookup-page-objects state) + selected (lookup-selected-raw state) + modifiers (:workspace-modifiers state) + + children-ids (cph/get-children-ids objects parent-id) + selected-children (into [] (filter selected) children-ids) + + modifiers (select-keys modifiers selected-children) + children (select-keys objects children-ids)] + + (as-> children $ + (gsh/merge-modifiers $ modifiers) + (d/mapm (set-content-modifiers state) $)))) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 839ff694c..24b48c0aa 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -9,9 +9,7 @@ (:require [app.common.data :as d] [app.common.data.macros :as dm] - [app.common.geom.shapes :as gsh] [app.common.pages.helpers :as cph] - [app.common.path.commands :as upc] [app.main.data.workspace.state-helpers :as wsh] [app.main.store :as st] [okulary.core :as l])) @@ -193,28 +191,29 @@ (assoc :pages (:pages data))))) st/state =)) +(def workspace-data + (l/derived :workspace-data st/state)) + (def workspace-file-colors - (l/derived (fn [state] - (when-let [file (:workspace-data state)] - (->> (:colors file) - (d/mapm #(assoc %2 :file-id (:id file)))))) - st/state)) + (l/derived (fn [data] + (when data + (->> (:colors data) + (d/mapm #(assoc %2 :file-id (:id data)))))) + workspace-data + =)) (def workspace-recent-colors - (l/derived (fn [state] - (dm/get-in state [:workspace-data :recent-colors] [])) - st/state)) + (l/derived (fn [data] + (get data :recent-colors [])) + workspace-data)) (def workspace-recent-fonts - (l/derived (fn [state] - (dm/get-in state [:workspace-data :recent-fonts] [])) - st/state)) + (l/derived (fn [data] + (get data :workspace-data [])) + workspace-data)) (def workspace-file-typography - (l/derived (fn [state] - (when-let [file (:workspace-data state)] - (:typographies file))) - st/state)) + (l/derived :typographies workspace-data)) (def workspace-project (l/derived :workspace-project st/state)) @@ -313,24 +312,8 @@ workspace-modifiers-with-objects =)) -(defn- set-content-modifiers [state] - (fn [id shape] - (let [content-modifiers (dm/get-in state [:workspace-local :edit-path id :content-modifiers])] - (if (some? content-modifiers) - (update shape :content upc/apply-content-modifiers content-modifiers) - shape)))) - (defn select-bool-children [id] - (let [selector - (fn [state] - (let [objects (wsh/lookup-page-objects state) - modifiers (:workspace-modifiers state) - children (->> (cph/get-children-ids objects id) - (select-keys objects))] - (as-> children $ - (gsh/merge-modifiers $ modifiers) - (d/mapm (set-content-modifiers state) $))))] - (l/derived selector st/state =))) + (l/derived (partial wsh/select-bool-children id) st/state =)) (def selected-data (l/derived #(let [selected (wsh/lookup-selected %) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index b5cf278f2..c3b957401 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -7,6 +7,7 @@ (ns app.main.ui.shapes.filters (:require [app.common.data :as d] + [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] [app.common.math :as mth] [app.common.uuid :as uuid] @@ -249,6 +250,7 @@ :height filter-height :filterUnits "objectBoundingBox" :color-interpolation-filters "sRGB"} - (for [entry filters] - [:& filter-entry {:entry entry}])]))) + (for [[index entry] (d/enumerate filters)] + [:& filter-entry {:key (dm/str filter-id "-" index) + :entry entry}])]))) diff --git a/frontend/src/app/main/ui/shapes/text/svg_text.cljs b/frontend/src/app/main/ui/shapes/text/svg_text.cljs index 79d1127f1..4c8054c24 100644 --- a/frontend/src/app/main/ui/shapes/text/svg_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/svg_text.cljs @@ -64,6 +64,7 @@ ;; These position attributes are not really necesary but they are convenient for for the export group-props (-> #js {:transform transform + :className "text-container" :x x :y y :width width diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index e465205c7..14620a762 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -81,6 +81,7 @@ (let [shape-node (dom/query base-node (str "#shape-" id)) frame? (= :frame type) + text? (= :text type) group? (= :group type) mask? (and group? masked-group?)] @@ -103,6 +104,10 @@ (dom/query-all shape-defs ".svg-def") (dom/query-all shape-defs ".svg-mask-wrapper"))) + text? + [shape-node + (dom/query shape-node ".text-container")] + :else [shape-node]))) @@ -161,6 +166,10 @@ (str value))] (dom/set-attribute! node att (str new-value)))) +(defn override-transform-att! + [node att value] + (dom/set-attribute! node att (str value))) + (defn update-transform! [base-node shapes transforms modifiers] (doseq [{:keys [id] :as shape} shapes] @@ -179,6 +188,17 @@ (dom/class? node "frame-children") (set-transform-att! node "transform" (gmt/inverse transform)) + ;; We need to update the shape transform matrix when there is a resize + ;; we do it dinamicaly here + (dom/class? node "text-container") + (let [modifiers (dissoc modifiers :displacement :rotation)] + (when (not (gsh/empty-modifiers? modifiers)) + (let [mtx (-> shape + (assoc :modifiers modifiers) + (gsh/transform-shape) + (gsh/transform-matrix {:no-flip true}))] + (override-transform-att! node "transform" mtx)))) + (or (= (dom/get-tag-name node) "mask") (= (dom/get-tag-name node) "filter")) (transform-region! node modifiers) diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index 1fe11720e..1c22c42d1 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -166,7 +166,8 @@ (:name frame)]])) (mf/defc frame-titles - {::mf/wrap-props false} + {::mf/wrap-props false + ::mf/wrap [mf/memo]} [props] (let [objects (unchecked-get props "objects") zoom (unchecked-get props "zoom") From 3930be5d9e7da7ac7199673789a45d0fda979036 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 12 May 2022 13:37:21 +0200 Subject: [PATCH 54/98] :bug: Remove warnings from external library --- frontend/src/app/util/dom/normalize_wheel.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/app/util/dom/normalize_wheel.js b/frontend/src/app/util/dom/normalize_wheel.js index c9f22399f..fc221e54c 100644 --- a/frontend/src/app/util/dom/normalize_wheel.js +++ b/frontend/src/app/util/dom/normalize_wheel.js @@ -6,8 +6,6 @@ * LICENSE file in the root directory of this source tree. An additional grant * of patent rights can be found in the PATENTS file in the same directory. * - * @providesModule normalizeWheel - * @typechecks */ /** From d8d4ce7a46f3468bee7ab40b5b598071ff2464c4 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 12 May 2022 16:32:25 +0200 Subject: [PATCH 55/98] :bug: Fix linter --- frontend/src/app/main/ui/shapes/shape.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index 790776004..6ec138194 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -41,7 +41,7 @@ (defn propagate-wrapper-styles ([children wrapper-props] (if (.isArray js/Array children) - (->> children (map propagate-wrapper-styles-child)) + (->> children (map #(propagate-wrapper-styles-child % wrapper-props))) (-> children (propagate-wrapper-styles-child wrapper-props))))) (mf/defc shape-container From 74612178d7d961ab8d12e5ce32fde6d320365695 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Thu, 12 May 2022 14:04:36 +0200 Subject: [PATCH 56/98] :bug: Fix Thumbnail blur on mouse movements --- .../shapes/frame/thumbnail_render.cljs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 1fb12c750..f603202c6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -17,19 +17,28 @@ [beicon.core :as rx] [rumext.alpha :as mf])) +(def thumbnail-scale-factor 2) + (defn- draw-thumbnail-canvas [canvas-node img-node] - (try - (when (and (some? canvas-node) (some? img-node)) - (let [canvas-context (.getContext canvas-node "2d") - canvas-width (.-width canvas-node) - canvas-height (.-height canvas-node)] - (.clearRect canvas-context 0 0 canvas-width canvas-height) - (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - (.toDataURL canvas-node "image/png"))) - (catch :default err - (.error js/console err) - nil))) + (time (try + (when (and (some? canvas-node) (some? img-node)) + (let [canvas-context (.getContext canvas-node "2d") + canvas-width (.-width canvas-node) + canvas-height (.-height canvas-node)] + + (set! (.-width canvas-node) (* thumbnail-scale-factor canvas-width)) + (set! (.-height canvas-node) (* thumbnail-scale-factor canvas-height)) + (.setTransform canvas-context thumbnail-scale-factor 0 0 thumbnail-scale-factor 0 0) + (set! (.-imageSmoothingEnabled canvas-context) true) + (set! (.-imageSmoothingQuality canvas-context) "high") + + (.clearRect canvas-context 0 0 canvas-width canvas-height) + (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (.toDataURL canvas-node "image/png" 1.0))) + (catch :default err + (.error js/console err) + nil)))) (defn use-render-thumbnail "Hook that will create the thumbnail thata" From d2983c11102e915fb947670cb2d8e097f2c51b3f Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 12 May 2022 17:32:51 +0200 Subject: [PATCH 57/98] :bug: Improve active frame behaviour for thumbnails --- .../src/app/main/ui/workspace/viewport.cljs | 2 +- .../app/main/ui/workspace/viewport/hooks.cljs | 74 +++++++++++++------ 2 files changed, 53 insertions(+), 23 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index 0224855ac..33419511a 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -183,7 +183,7 @@ (hooks/setup-hover-shapes page-id move-stream base-objects transform selected mod? hover hover-ids @hover-disabled? focus zoom) (hooks/setup-viewport-modifiers modifiers base-objects) (hooks/setup-shortcuts node-editing? drawing-path?) - (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform) + (hooks/setup-active-frames base-objects hover-ids selected active-frames zoom transform vbox) [:div.viewport [:div.viewport-overlays {:ref overlays-ref} diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 8cd4fda9c..9fad99089 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -214,35 +214,65 @@ (defn inside-vbox [vbox objects frame-id] (let [frame (get objects frame-id)] - - (and (some? frame) - (gsh/overlaps? frame vbox)))) + (and (some? frame) (gsh/overlaps? frame vbox)))) (defn setup-active-frames - [objects hover-ids selected active-frames zoom transform] + [objects hover-ids selected active-frames zoom transform vbox] - (mf/use-effect - (mf/deps objects @hover-ids selected zoom transform) - (fn [] - (when (some? @hover-ids) - (let [hover-frame (when (> zoom 0.25) (last @hover-ids)) - new-active-frames (if (some? hover-frame) #{hover-frame} #{}) + (let [frame? #(= :frame (get-in objects [% :type])) + all-frames (mf/use-memo (mf/deps objects) #(cph/get-frames-ids objects)) + selected-frames (mf/use-memo (mf/deps selected) #(->> all-frames (filter selected))) + xf-selected-frame (comp (remove frame?) (map #(get-in objects [% :frame-id]))) + selected-shapes-frames (mf/use-memo (mf/deps selected) #(into #{} xf-selected-frame selected)) - frame? #(= :frame (get-in objects [% :type])) + active-selection (when (and (not= transform :move) (= (count selected-frames) 1)) (first selected-frames)) + hover-frame (last @hover-ids) + last-hover-frame (mf/use-var nil)] - selected-frames (->> selected (filter frame?)) - new-active-frames - (cond-> new-active-frames - (and (not= transform :move) (= (count selected-frames) 1)) - (conj new-active-frames (first selected-frames))) + (mf/use-effect + (mf/deps hover-frame) + (fn [] + (when (some? hover-frame) + (reset! last-hover-frame hover-frame)))) + + (mf/use-effect + (mf/deps objects @hover-ids selected zoom transform vbox) + (fn [] + + ;; Rules for active frame: + ;; - If zoom < 25% displays thumbnail except when selecting a single frame or a child + ;; - We always active the current hovering frame for zoom > 25% + ;; - When zoom > 150% we activate the frames that are inside the vbox + ;; - If no hovering over any frames we keep the previous active one + ;; - Check always that the active frames are inside the vbox + + (let [is-active-frame? + (fn [id] + (or + ;; Zoom > 150% shows every frame + (> zoom 1.5) + + ;; Zoom >= 30% will show frames hovering + (and + (>= zoom 0.3) + (or (= id hover-frame) (= id @last-hover-frame))) + + ;; Otherwise, if it's a selected frame + (= id active-selection) + + ;; Or contains a selected shape + (contains? selected-shapes-frames id))) new-active-frames - (into new-active-frames - (comp - (remove frame?) - (map #(get-in objects [% :frame-id]))) - selected)] - (reset! active-frames new-active-frames)))))) + (into #{} + (comp (filter is-active-frame?) + + ;; We only allow active frames that are contained in the vbox + (filter (partial inside-vbox vbox objects))) + all-frames)] + + (when (not= @active-frames new-active-frames) + (reset! active-frames new-active-frames))))))) ;; NOTE: this is executed on each page change, maybe we need to move ;; this shortcuts outside the viewport? From ca2eb1ac12d4d0a62ca2294465fbeb6d4a32fcf6 Mon Sep 17 00:00:00 2001 From: elhombretecla Date: Thu, 12 May 2022 22:02:21 +0200 Subject: [PATCH 58/98] :lipstick: Add new onboarding texts --- frontend/src/app/main/ui/releases/v1_13.cljs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/frontend/src/app/main/ui/releases/v1_13.cljs b/frontend/src/app/main/ui/releases/v1_13.cljs index 4678e9782..9f73b5729 100644 --- a/frontend/src/app/main/ui/releases/v1_13.cljs +++ b/frontend/src/app/main/ui/releases/v1_13.cljs @@ -24,7 +24,7 @@ [:h2 "What's new?"]] [:span.release "Beta version " version] [:div.modal-content - [:p "Penpot continues growing with new features that improve performance, user experience and visual design."] + [:p "Penpot continues to grow with new features that improve performance, user experience and visual design."] [:p "We are happy to show you a sneak peak of the most important stuff that the Beta 1.13 version brings."]] [:div.modal-navigation [:button.btn-secondary {:on-click next} "Continue"]]] @@ -41,8 +41,8 @@ [:div.modal-title [:h2 "Multiple exports"]] [:div.modal-content - [:p "Speed your workflow exporting multiple elements simultaneously."] - [:p "Use the export window to manage your multiple exports and be informed about the download progress. Big exports will happen in the background so you can continue designing in the meantime ;)"]] + [:p "Speed up your workflow exporting multiple elements simultaneously."] + [:p "Use the export window to manage your multiple exports and be informed about the download progress. Big exports will happen in the background so you can keep designing in the meantime ;)"]] [:div.modal-navigation [:button.btn-secondary {:on-click next} "Continue"] [:& c/navigation-bullets @@ -61,7 +61,7 @@ [:h2 "Multiple fills and strokes"]] [:div.modal-content [:p "Now you can add multiple color fills and strokes to a single element, including shapes and texts."] - [:p "This opens endless graphic possibilities such as combining gradients and blending modes in the same element to create visual effects."]] + [:p "This opens endless graphic possibilities such as combining gradients and blending modes in the same element to create sophisticated visual effects."]] [:div.modal-navigation [:button.btn-secondary {:on-click next} "Continue"] [:& c/navigation-bullets @@ -80,7 +80,7 @@ [:h2 "Members area redesign"]] [:div.modal-content [:p "Penpot is meant for teams, that’s why we decided to give some love to the members area."] - [:p "A refreshed interface and two new features: the Invitations section where you can check the state of the team invites and the ability to invite multiple members at the same time."]] + [:p "A refreshed interface and two new features! The Invitations section allows you to check the status of current team invites plus you now have the ability to invite multiple members at the same time."]] [:div.modal-navigation [:button.btn-secondary {:on-click next} "Continue"] [:& c/navigation-bullets @@ -98,8 +98,8 @@ [:div.modal-title [:h2 "Focus mode"]] [:div.modal-content - [:p "Select the elements of a page you want to work with in a specific moment hiding the rest so they don’t get in the way of your attention."] - [:p "This option is also useful to improve the performance in cases where the page has a large number of elements."]] + [:p "Enjoy a distraction-less design mode by selecting the elements of a page that matter to you and temporarily hiding the rest."] + [:p "As a side effect, this can give you a performance boost in massive designs."]] [:div.modal-navigation [:button.btn-secondary {:on-click finish} "Start!"] [:& c/navigation-bullets From c84017eb72049165884514f5a46945cb37695d19 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 13 May 2022 07:58:02 +0200 Subject: [PATCH 59/98] :bug: Fix multiple fills with texts are not working properly --- frontend/src/app/main/data/workspace/texts.cljs | 11 ++++++----- frontend/src/app/main/ui/shapes/attrs.cljs | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index 0d46f915a..b9545818f 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -246,12 +246,13 @@ [node] (let [color-attrs (select-keys node [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient])] (cond-> node - (d/not-empty? color-attrs) - (-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient) - (assoc :fills [color-attrs])) - (nil? (:fills node)) - (assoc :fills (:fills txt/default-text-attrs))))) + (assoc :fills (:fills txt/default-text-attrs)) + + (and (d/not-empty? color-attrs) (nil? (:fills node))) + (-> (dissoc :fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient) + (assoc :fills [color-attrs]))) + )) (defn migrate-content [content] diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index d39ba43f2..9de6b4ffc 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -66,7 +66,7 @@ (let [fill-image-id (str "fill-image-" render-id)] {:fill (str "url(#" fill-image-id ")")}) - (contains? shape :fill-color-gradient) + (and (contains? shape :fill-color-gradient) (some? (:fill-color-gradient shape))) (let [fill-color-gradient-id (str "fill-color-gradient_" render-id (if index (str "_" index) ""))] {:fill (str "url(#" fill-color-gradient-id ")")}) From 448e0dd415a6a839e8444b94705aab2bd9713c5e Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Fri, 13 May 2022 11:27:47 +0200 Subject: [PATCH 60/98] :bug: Fix artboards thumbnail in another page --- frontend/src/app/main/data/workspace/changes.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/changes.cljs b/frontend/src/app/main/data/workspace/changes.cljs index 6d8557f5f..c8f1f166c 100644 --- a/frontend/src/app/main/data/workspace/changes.cljs +++ b/frontend/src/app/main/data/workspace/changes.cljs @@ -45,7 +45,7 @@ ptk/WatchEvent (watch [it state _] (let [page-id (or page-id (:current-page-id state)) - objects (wsh/lookup-page-objects state) + objects (wsh/lookup-page-objects state page-id) ids (into [] (filter some?) ids) changes (reduce From db8e829339bc896a0173dfbf71b1d2827c7b40ac Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 13 May 2022 12:00:18 +0200 Subject: [PATCH 61/98] :bug: Fix remove time debug info --- .../shapes/frame/thumbnail_render.cljs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index f603202c6..89dc31376 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -21,24 +21,24 @@ (defn- draw-thumbnail-canvas [canvas-node img-node] - (time (try - (when (and (some? canvas-node) (some? img-node)) - (let [canvas-context (.getContext canvas-node "2d") - canvas-width (.-width canvas-node) - canvas-height (.-height canvas-node)] + (try + (when (and (some? canvas-node) (some? img-node)) + (let [canvas-context (.getContext canvas-node "2d") + canvas-width (.-width canvas-node) + canvas-height (.-height canvas-node)] - (set! (.-width canvas-node) (* thumbnail-scale-factor canvas-width)) - (set! (.-height canvas-node) (* thumbnail-scale-factor canvas-height)) - (.setTransform canvas-context thumbnail-scale-factor 0 0 thumbnail-scale-factor 0 0) - (set! (.-imageSmoothingEnabled canvas-context) true) - (set! (.-imageSmoothingQuality canvas-context) "high") + (set! (.-width canvas-node) (* thumbnail-scale-factor canvas-width)) + (set! (.-height canvas-node) (* thumbnail-scale-factor canvas-height)) + (.setTransform canvas-context thumbnail-scale-factor 0 0 thumbnail-scale-factor 0 0) + (set! (.-imageSmoothingEnabled canvas-context) true) + (set! (.-imageSmoothingQuality canvas-context) "high") - (.clearRect canvas-context 0 0 canvas-width canvas-height) - (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) - (.toDataURL canvas-node "image/png" 1.0))) - (catch :default err - (.error js/console err) - nil)))) + (.clearRect canvas-context 0 0 canvas-width canvas-height) + (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) + (.toDataURL canvas-node "image/png" 1.0))) + (catch :default err + (.error js/console err) + nil))) (defn use-render-thumbnail "Hook that will create the thumbnail thata" From 3f413e49206c2267d2a20d0843e4e58a85d5eee4 Mon Sep 17 00:00:00 2001 From: Eva Date: Fri, 13 May 2022 12:44:11 +0200 Subject: [PATCH 62/98] :bug: Show strokes and fills in text when in handoff --- .../resources/styles/main/partials/handoff.scss | 2 +- .../src/app/main/ui/viewer/handoff/attributes.cljs | 2 +- .../main/ui/viewer/handoff/attributes/fill.cljs | 4 ++-- .../main/ui/viewer/handoff/attributes/stroke.cljs | 4 ++-- .../main/ui/viewer/handoff/attributes/text.cljs | 14 ++++++++------ 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/frontend/resources/styles/main/partials/handoff.scss b/frontend/resources/styles/main/partials/handoff.scss index 6a2b949d1..adcb45082 100644 --- a/frontend/resources/styles/main/partials/handoff.scss +++ b/frontend/resources/styles/main/partials/handoff.scss @@ -144,7 +144,7 @@ .color-bullet { width: 24px; height: 24px; - border-radius: $br-small; + border-radius: 50%; border: 1px solid $color-gray-60; } diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs index ae9e5fe93..69ac8723f 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs @@ -27,7 +27,7 @@ :circle [:layout :fill :stroke :shadow :blur :svg] :path [:layout :fill :stroke :shadow :blur :svg] :image [:image :layout :fill :stroke :shadow :blur :svg] - :text [:layout :text :shadow :blur]}) + :text [:layout :text :shadow :blur :stroke]}) (mf/defc attributes [{:keys [page-id file-id shapes frame]}] diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs index f24df7b6e..878014efa 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/fill.cljs @@ -58,7 +58,7 @@ (for [shape shapes] (if (seq (:fills shape)) (for [value (:fills shape [])] - [:& fill-block {:key (str "fill-block-" (:id shape)) + [:& fill-block {:key (str "fill-block-" (:id shape) value) :shape value}]) - [:& fill-block {:key (str "fill-block-" (:id shape)) + [:& fill-block {:key (str "fill-block-only" (:id shape)) :shape shape}]))]))) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs index a5fea5283..bb4158060 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/stroke.cljs @@ -84,7 +84,7 @@ (for [shape shapes] (if (seq (:strokes shape)) (for [value (:strokes shape [])] - [:& stroke-block {:key (str "stroke-color-" (:id shape)) + [:& stroke-block {:key (str "stroke-color-" (:id shape) value) :shape value}]) - [:& stroke-block {:key (str "stroke-color-" (:id shape)) + [:& stroke-block {:key (str "stroke-color-only" (:id shape)) :shape shape}]))]))) diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs index 2cfdda7bd..9fd0cda4c 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes/text.cljs @@ -101,12 +101,13 @@ [:div.attributes-content-row [:pre.attributes-content (str/trim text)] [:& copy-button {:data (str/trim text)}]] + (when (:fills style) + (for [fill (:fills style)] - (when (or (:fill-color style) (:fill-color-gradient style)) - [:& color-row {:format @color-format - :color (shape->color style) - :copy-data (copy-style-data style :fill-color :fill-color-gradient) - :on-change-format #(reset! color-format %)}]) + [:& color-row {:format @color-format + :color (shape->color fill) + :copy-data (copy-style-data fill :fill-color :fill-color-gradient) + :on-change-format #(reset! color-format %)}])) (when (:font-id style) [:div.attributes-unit-row @@ -186,4 +187,5 @@ [:div.attributes-block-title-text (tr "handoff.attributes.typography")]] (for [shape shapes] - [:& text-block {:shape shape}])])) + [:& text-block {:shape shape + :key (str "text-block" (:id shape))}])])) From 95d40180741b8beae27e1fd3e8a8ae74b2861437 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 13 May 2022 09:36:45 +0200 Subject: [PATCH 63/98] :bug: Fix paste svg with empty space --- .../src/app/main/data/workspace/svg_upload.cljs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index 9f8147e11..df39c8bc2 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -463,8 +463,23 @@ root-shape (create-svg-root frame-id svg-data) root-id (:id root-shape) + ;; In penpot groups have the size of their children. To respect the imported svg size and empty space let's create a transparent shape as background to respect the imported size + base-background-shape {:tag :rect + :attrs {:x "0" + :y "0" + :width (str (:width root-shape)) + :height (str (:height root-shape)) + :fill "none" + :id "base-background"} + :content []} + + svg-data (-> svg-data + (assoc :defs def-nodes) + (assoc :content (into [base-background-shape] (:content svg-data)))) + ;; Creates the root shape new-shape (dwc/make-new-shape root-shape objects selected) + changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) (pcb/add-object new-shape)) @@ -478,7 +493,6 @@ [unames changes] (d/enumerate (->> (:content svg-data) (mapv #(usvg/inherit-attributes root-attrs %))))) - changes (pcb/resize-parents changes (->> changes :redo-changes From ea50622bf74b871cd875815b1044c87ff9e8cb0e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 13:16:36 +0200 Subject: [PATCH 64/98] :bug: Fine tune thumbnails --- .../ui/workspace/shapes/frame/thumbnail_render.cljs | 13 +++++++------ .../src/app/main/ui/workspace/viewport/hooks.cljs | 12 ++++++------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 89dc31376..2aaceb8c8 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -17,7 +17,7 @@ [beicon.core :as rx] [rumext.alpha :as mf])) -(def thumbnail-scale-factor 2) +;; (def thumbnail-scale-factor 2) (defn- draw-thumbnail-canvas [canvas-node img-node] @@ -27,11 +27,12 @@ canvas-width (.-width canvas-node) canvas-height (.-height canvas-node)] - (set! (.-width canvas-node) (* thumbnail-scale-factor canvas-width)) - (set! (.-height canvas-node) (* thumbnail-scale-factor canvas-height)) - (.setTransform canvas-context thumbnail-scale-factor 0 0 thumbnail-scale-factor 0 0) - (set! (.-imageSmoothingEnabled canvas-context) true) - (set! (.-imageSmoothingQuality canvas-context) "high") + ;; TODO: Expermient with different scale factors + ;; (set! (.-width canvas-node) (* thumbnail-scale-factor canvas-width)) + ;; (set! (.-height canvas-node) (* thumbnail-scale-factor canvas-height)) + ;; (.setTransform canvas-context thumbnail-scale-factor 0 0 thumbnail-scale-factor 0 0) + ;; (set! (.-imageSmoothingEnabled canvas-context) true) + ;; (set! (.-imageSmoothingQuality canvas-context) "high") (.clearRect canvas-context 0 0 canvas-width canvas-height) (.drawImage canvas-context img-node 0 0 canvas-width canvas-height) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 9fad99089..856794da4 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -242,19 +242,19 @@ ;; Rules for active frame: ;; - If zoom < 25% displays thumbnail except when selecting a single frame or a child ;; - We always active the current hovering frame for zoom > 25% - ;; - When zoom > 150% we activate the frames that are inside the vbox + ;; - When zoom > 130% we activate the frames that are inside the vbox ;; - If no hovering over any frames we keep the previous active one ;; - Check always that the active frames are inside the vbox (let [is-active-frame? (fn [id] (or - ;; Zoom > 150% shows every frame - (> zoom 1.5) + ;; Zoom > 130% shows every frame + (> zoom 1.3) - ;; Zoom >= 30% will show frames hovering + ;; Zoom >= 25% will show frames hovering (and - (>= zoom 0.3) + (>= zoom 0.25) (or (= id hover-frame) (= id @last-hover-frame))) ;; Otherwise, if it's a selected frame @@ -272,7 +272,7 @@ all-frames)] (when (not= @active-frames new-active-frames) - (reset! active-frames new-active-frames))))))) + (reset! active-frames #{} #_new-active-frames))))))) ;; NOTE: this is executed on each page change, maybe we need to move ;; this shortcuts outside the viewport? From 4730273ad366ab77f4f5ee6f6f6a32af2f9a8327 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 13:32:22 +0200 Subject: [PATCH 65/98] :bug: Rollback thumbnail problem --- frontend/src/app/main/ui/workspace/viewport/hooks.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index 856794da4..9f628336d 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -272,7 +272,7 @@ all-frames)] (when (not= @active-frames new-active-frames) - (reset! active-frames #{} #_new-active-frames))))))) + (reset! active-frames new-active-frames))))))) ;; NOTE: this is executed on each page change, maybe we need to move ;; this shortcuts outside the viewport? From abdc9b2cbdfd5f2a152909e52812b91dfcb00990 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 13:08:23 +0200 Subject: [PATCH 66/98] :bug: Fix problem with center vertical align and auto-height --- frontend/src/app/main/refs.cljs | 2 +- .../workspace/shapes/text/viewport_texts.cljs | 23 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/main/refs.cljs b/frontend/src/app/main/refs.cljs index 24b48c0aa..91117c622 100644 --- a/frontend/src/app/main/refs.cljs +++ b/frontend/src/app/main/refs.cljs @@ -392,4 +392,4 @@ (l/derived :workspace-text-modifier st/state)) (defn workspace-text-modifier-by-id [id] - (l/derived #(get % id) workspace-text-modifier)) + (l/derived #(get % id) workspace-text-modifier =)) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index fea2ad1d9..a2bcc391e 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -87,7 +87,8 @@ (st/emit! (dwt/update-text-modifier id props)))) (mf/defc text-container - {::mf/wrap-props false} + {::mf/wrap-props false + ::mf/wrap [mf/memo]} [props] (let [shape (obj/get props "shape") on-update (obj/get props "on-update") @@ -98,6 +99,7 @@ (fn [node] (when (some? node) (on-update shape node))))] + [:& fo/text-shape {:key (str "shape-" (:id shape)) :ref handle-update :shape shape @@ -144,10 +146,29 @@ (-> (mf/deref refs/workspace-editor-state) (get (:id shape))) + text-modifier-ref + (mf/use-memo (mf/deps (:id shape)) #(refs/workspace-text-modifier-by-id (:id shape))) + + text-modifier + (mf/deref text-modifier-ref) + shape (cond-> shape (some? editor-state) (update-with-editor-state editor-state)) + ;; When we have a text with grow-type :auto-height we need to check the correct height + ;; otherwise the center alignment will break + shape + (if (or (not= :auto-height (:grow-type shape)) (empty? text-modifier)) + shape + (let [tr-shape (dwt/apply-text-modifier shape text-modifier)] + (cond-> shape + ;; we only change the height otherwise could cause problems with the other fields + (some? text-modifier) + (assoc :height (:height tr-shape))))) + + shape (hooks/use-equal-memo shape) + handle-update-shape (mf/use-callback update-text-modifier)] (mf/use-effect From ae84f3cbe8638c31e473808155dd57a9695be9be Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 16:02:40 +0200 Subject: [PATCH 67/98] :bug: Fix typo in debug option --- frontend/src/app/worker/thumbnails.cljs | 2 +- frontend/src/debug.cljs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/src/app/worker/thumbnails.cljs b/frontend/src/app/worker/thumbnails.cljs index 832577364..92bf40cde 100644 --- a/frontend/src/app/worker/thumbnails.cljs +++ b/frontend/src/app/worker/thumbnails.cljs @@ -116,7 +116,7 @@ (rx/map render-thumbnail) (rx/mapcat persist-thumbnail)))] - (if (debug? :disable-thumbnail-cachee) + (if (debug? :disable-thumbnail-cache) (->> (request-data-for-thumbnail file-id revn) (rx/map render-thumbnail)) (->> (request-thumbnail file-id revn) diff --git a/frontend/src/debug.cljs b/frontend/src/debug.cljs index e86e324cc..66ef857ee 100644 --- a/frontend/src/debug.cljs +++ b/frontend/src/debug.cljs @@ -63,7 +63,7 @@ :text-outline ;; Disable thumbnail cache - :disable-thumbnail-cachee + :disable-thumbnail-cache }) ;; These events are excluded when we activate the :events flag From 570f03806245665a70a8fc44ddee294cc19e8a73 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 16:03:06 +0200 Subject: [PATCH 68/98] :bug: Disable stroke style for texts --- .../sidebar/options/menus/stroke.cljs | 5 +++-- .../sidebar/options/rows/stroke_row.cljs | 19 ++++++++++--------- .../sidebar/options/shapes/multiple.cljs | 5 ++++- .../sidebar/options/shapes/text.cljs | 3 ++- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs index e18ad8832..a7c0e619c 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/stroke.cljs @@ -34,7 +34,7 @@ (mf/defc stroke-menu {::mf/wrap [#(mf/memo' % (mf/check-props ["ids" "values" "type" "show-caps"]))]} - [{:keys [ids type values show-caps] :as props}] + [{:keys [ids type values show-caps disable-stroke-style] :as props}] (let [label (case type :multiple (tr "workspace.options.selection-stroke") :group (tr "workspace.options.group-stroke") @@ -191,4 +191,5 @@ :on-reorder (handle-reorder index) :disable-drag disable-drag :select-all select-all - :on-blur on-blur}])])]])) + :on-blur on-blur + :disable-stroke-style disable-stroke-style}])])]])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs index c27501057..449ac8235 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/rows/stroke_row.cljs @@ -47,7 +47,7 @@ (second)))) (mf/defc stroke-row - [{:keys [index stroke title show-caps on-color-change on-reorder on-color-detach on-remove on-stroke-width-change on-stroke-style-change on-stroke-alignment-change open-caps-select close-caps-select on-stroke-cap-start-change on-stroke-cap-end-change on-stroke-cap-switch disable-drag select-all on-blur]}] + [{:keys [index stroke title show-caps on-color-change on-reorder on-color-detach on-remove on-stroke-width-change on-stroke-style-change on-stroke-alignment-change open-caps-select close-caps-select on-stroke-cap-start-change on-stroke-cap-end-change on-stroke-cap-switch disable-drag select-all on-blur disable-stroke-style]}] (let [start-caps-state (mf/use-state {:open? false :top 0 :left 0}) @@ -110,14 +110,15 @@ [:option {:value ":inner"} (tr "workspace.options.stroke.inner")] [:option {:value ":outer"} (tr "workspace.options.stroke.outer")]] - [:select#style.input-select {:value (enum->string (:stroke-style stroke)) - :on-change (on-stroke-style-change index)} - (when (= (:stroke-style stroke) :multiple) - [:option {:value ""} "--"]) - [:option {:value ":solid"} (tr "workspace.options.stroke.solid")] - [:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")] - [:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")] - [:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]]] + (when-not disable-stroke-style + [:select#style.input-select {:value (enum->string (:stroke-style stroke)) + :on-change (on-stroke-style-change index)} + (when (= (:stroke-style stroke) :multiple) + [:option {:value ""} "--"]) + [:option {:value ":solid"} (tr "workspace.options.stroke.solid")] + [:option {:value ":dotted"} (tr "workspace.options.stroke.dotted")] + [:option {:value ":dashed"} (tr "workspace.options.stroke.dashed")] + [:option {:value ":mixed"} (tr "workspace.options.stroke.mixed")]])] ;; Stroke Caps (when show-caps diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs index 7b4754e02..d962787e1 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/multiple.cljs @@ -240,6 +240,8 @@ type :multiple all-types (into #{} (map :type shapes)) + has-text? (contains? all-types :text) + [measure-ids measure-values] (get-attrs shapes objects :measure) [layer-ids layer-values @@ -279,7 +281,8 @@ [:& fill-menu {:type type :ids fill-ids :values fill-values}]) (when-not (empty? stroke-ids) - [:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values}]) + [:& stroke-menu {:type type :ids stroke-ids :show-caps show-caps :values stroke-values + :disable-stroke-style has-text?}]) (when-not (empty? shadow-ids) [:& shadow-menu {:type type :ids shadow-ids :values shadow-values}]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs index 93324181a..62ab34475 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/shapes/text.cljs @@ -80,7 +80,8 @@ [:& stroke-menu {:ids ids :type type - :values stroke-values}] + :values stroke-values + :disable-stroke-style true}] [:& shadow-menu {:ids ids From 080139cd56b4aa46e8b1d6da625baf8f0de53a70 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 16:03:35 +0200 Subject: [PATCH 69/98] :bug: Improved performance for text resize --- .../ui/workspace/shapes/text/viewport_texts.cljs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index a2bcc391e..c5e38e957 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -110,6 +110,8 @@ ::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]} [props] (let [text-shapes (obj/get props "text-shapes") + modifiers (obj/get props "modifiers") + prev-modifiers (hooks/use-previous modifiers) prev-text-shapes (hooks/use-previous text-shapes) ;; A change in position-data won't be a "real" change @@ -117,8 +119,10 @@ (fn [id] (let [old-shape (get prev-text-shapes id) new-shape (get text-shapes id)] - (and (not (identical? old-shape new-shape)) - (not= old-shape new-shape)))) + (or (and (not (identical? old-shape new-shape)) + (not= old-shape new-shape)) + (not= (get modifiers id) + (get prev-modifiers id))))) changed-texts (mf/use-memo @@ -127,12 +131,15 @@ (filter text-change?) (map (d/getf text-shapes)))) + handle-update-modifier (mf/use-callback update-text-modifier) handle-update-shape (mf/use-callback update-text-shape)] [:* (for [{:keys [id] :as shape} changed-texts] [:& text-container {:shape (gsh/transform-shape shape) - :on-update handle-update-shape + :on-update (if (some? (get modifiers (:id shape))) + handle-update-modifier + handle-update-shape) :key (str (dm/str "text-container-" id))}])])) (mf/defc viewport-text-editing From a2de5f8fb426f210aeca9fe745df9ac2d37c725e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 13 May 2022 16:16:44 +0200 Subject: [PATCH 70/98] :bug: Fix center alignment with new lines --- frontend/src/app/main/ui/workspace/shapes/text/editor.cljs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs index 44ff09f65..ff2fd0bfc 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/editor.cljs @@ -152,7 +152,10 @@ (let [old-state (mf/ref-val prev-value)] (if (and (some? state) (some? old-state)) (let [block-changes (ted/get-content-changes old-state state) - prev-data (ted/get-editor-current-inline-styles old-state) + + prev-data (-> (ted/get-editor-current-inline-styles old-state) + (dissoc :text-align :text-direction)) + block-to-setup (get-blocks-to-setup block-changes) block-to-add-styles (get-blocks-to-add-styles block-changes)] (-> state From 799bb87398a985950acd4f089d7871c589840aa6 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 17 May 2022 10:00:13 +0200 Subject: [PATCH 71/98] :bug: Fix security concern --- frontend/src/app/util/svg.cljs | 2 -- 1 file changed, 2 deletions(-) diff --git a/frontend/src/app/util/svg.cljs b/frontend/src/app/util/svg.cljs index 229557257..198fd91c2 100644 --- a/frontend/src/app/util/svg.cljs +++ b/frontend/src/app/util/svg.cljs @@ -90,7 +90,6 @@ :polyline :radialGradient :rect - :script :set :stop :style @@ -495,7 +494,6 @@ :marker :mask :pattern - :script :style :switch :text From a240fbdf5b8fa204ee623b5f1fa88a479d8d830b Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 17 May 2022 10:29:19 +0200 Subject: [PATCH 72/98] :bug: Fix change layer position on group or component creation --- frontend/src/app/main/data/workspace/groups.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/groups.cljs b/frontend/src/app/main/data/workspace/groups.cljs index 52aa5e009..fc7d905be 100644 --- a/frontend/src/app/main/data/workspace/groups.cljs +++ b/frontend/src/app/main/data/workspace/groups.cljs @@ -88,7 +88,7 @@ changes (-> (pcb/empty-changes it page-id) (pcb/with-objects objects) - (pcb/add-object group) + (pcb/add-object group {:index (::index (first shapes))}) (pcb/change-parent (:id group) shapes) (pcb/remove-objects ids-to-delete))] From 141bcdd25ec36af73abc2aed056c1014c2cc2609 Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 17 May 2022 11:59:48 +0200 Subject: [PATCH 73/98] :bug: Fix rotation value when path is not rotated --- .../app/main/ui/workspace/sidebar/options/menus/measures.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index c581c2117..dfc8f1640 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -72,7 +72,7 @@ (not= (:width values) :multiple) (assoc :width width) (not= (:height values) :multiple) (assoc :height height))) - values (let [{:keys [rotation]} (-> shapes first)] + values (let [{:keys [rotation] :or {rotation 0}} (-> shapes first)] (cond-> values (not= (:rotation values) :multiple) (assoc :rotation rotation))) From 712c68fc77da2a5d6f99238053eef684376a4134 Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 17 May 2022 12:35:35 +0200 Subject: [PATCH 74/98] :bug: Fix typo --- CHANGES.md | 1 + frontend/translations/en.po | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 5e4f2f82e..c74f1ff35 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -48,6 +48,7 @@ ### :bug: Bugs fixed +- Fix typo in viewer comment section [Taiga #3401](https://tree.taiga.io/project/penpot/issue/3401) - Do not show team-up modal for users already on a team [Taiga #3311](https://tree.taiga.io/project/penpot/issue/3311) - Constraints are not well assigned when default and multiselection [Taiga #3069](https://tree.taiga.io/project/penpot/issue/3069) - Duplicate artboards create new flows if needed [Taiga #2221](https://tree.taiga.io/project/penpot/issue/2221) diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 9b8d65519..e0f1f6e5c 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1388,7 +1388,7 @@ msgstr "Show all comments" #: src/app/main/ui/workspace/comments.cljs, src/app/main/ui/viewer/header.cljs msgid "labels.show-your-comments" -msgstr "Show only yours comments" +msgstr "Show only your comments" #: src/app/main/ui/static.cljs msgid "labels.sign-out" From 216b510900bf6152b6e3e107a4633a1cbd6030d2 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Tue, 17 May 2022 12:56:06 +0200 Subject: [PATCH 75/98] :bug: Fix security concern --- frontend/src/app/main/ui/shapes/svg_raw.cljs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/svg_raw.cljs b/frontend/src/app/main/ui/shapes/svg_raw.cljs index ca950985e..690ee6022 100644 --- a/frontend/src/app/main/ui/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/shapes/svg_raw.cljs @@ -94,9 +94,10 @@ {:keys [content]} shape {:keys [tag]} content - svg-root? (and (map? content) (= tag :svg)) - svg-tag? (map? content) - svg-leaf? (string? content)] + svg-root? (and (map? content) (= tag :svg)) + svg-tag? (map? content) + svg-leaf? (string? content) + valid-tag? (contains? usvg/svg-tags-list tag)] (cond svg-root? @@ -104,12 +105,12 @@ (for [item childs] [:& shape-wrapper {:shape item :key (dm/str (:id item))}])] - svg-tag? + (and svg-tag? valid-tag?) [:& svg-element {:shape shape} (for [item childs] [:& shape-wrapper {:shape item :key (dm/str (:id item))}])] - svg-leaf? + (and svg-leaf? valid-tag?) content :else nil)))) From d684970bfbb6dfa0641c029cda9b15168351c960 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 10:36:30 +0200 Subject: [PATCH 76/98] :bug: Fix problem with single line texts --- .../src/app/main/ui/workspace/shapes/text/viewport_texts.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index c5e38e957..2e65fd79c 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -33,7 +33,7 @@ (let [modifier (get modifiers id) modifier (d/update-when modifier :modifiers dissoc :displacement :rotation) shape (cond-> shape - (not (gsh/empty-modifiers? modifier)) + (not (gsh/empty-modifiers? (:modifiers modifier))) (-> (assoc :grow-type :fixed) (merge modifier) gsh/transform-shape))] (strip-position-data shape))) From 5a07599fc781cb080944bdf51798f64452e3585c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 12:26:47 +0200 Subject: [PATCH 77/98] :bug: Fix problem with thumbnail re-rendering --- .../app/main/ui/workspace/shapes/frame.cljs | 15 +++++++++---- .../shapes/frame/thumbnail_render.cljs | 21 +++++++++---------- frontend/src/app/util/dom.cljs | 5 +++-- 3 files changed, 24 insertions(+), 17 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index a2b71dee7..782600633 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -10,6 +10,7 @@ [app.common.data.macros :as dm] [app.common.uuid :as uuid] [app.main.data.workspace.thumbnails :as dwt] + [app.main.fonts :as fonts] [app.main.refs :as refs] [app.main.ui.context :as ctx] [app.main.ui.hooks :as hooks] @@ -27,19 +28,17 @@ [shape-wrapper] (let [frame-shape (frame/frame-shape shape-wrapper)] (mf/fnc frame-shape-inner - {::mf/wrap [#(mf/memo' % (mf/check-props ["shape" "fonts"]))] + {::mf/wrap [#(mf/memo' % (mf/check-props ["shape"]))] ::mf/wrap-props false ::mf/forward-ref true} [props ref] (let [shape (unchecked-get props "shape") - fonts (unchecked-get props "fonts") childs-ref (mf/use-memo (mf/deps (:id shape)) #(refs/children-objects (:id shape))) childs (mf/deref childs-ref)] [:& (mf/provider embed/context) {:value true} [:& shape-container {:shape shape :ref ref} - [:& ff/fontfaces-style {:fonts fonts}] [:& frame-shape {:shape shape :childs childs} ]]])))) (defn check-props @@ -90,13 +89,20 @@ disable-thumbnail? (d/not-empty? (dm/get-in modifiers [(:id shape) :modifiers])) [on-load-frame-dom thumb-renderer] - (ftr/use-render-thumbnail page-id shape node-ref rendered? thumbnail? disable-thumbnail?) + (ftr/use-render-thumbnail page-id shape node-ref rendered? thumbnail-data-ref disable-thumbnail?) on-frame-load (fns/use-node-store thumbnail? node-ref rendered?)] (fdm/use-dynamic-modifiers objects @node-ref modifiers) + (mf/use-effect + (mf/deps fonts) + (fn [] + (->> (rx/from fonts) + (rx/merge-map fonts/fetch-font-css) + (rx/ignore)))) + (mf/use-effect (fn [] ;; When a change in the data is received a "force-render" event is emited @@ -121,6 +127,7 @@ [:& (mf/provider ctx/render-ctx) {:value render-id} [:g.frame-container {:key "frame-container" :ref on-frame-load} + [:& ff/fontfaces-style {:fonts fonts}] [:g.frame-thumbnail-wrapper {:id (dm/str "thumbnail-container-" (:id shape))} [:> frame/frame-thumbnail {:key (dm/str (:id shape)) :shape (cond-> shape diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 2aaceb8c8..002926e8d 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -43,7 +43,7 @@ (defn use-render-thumbnail "Hook that will create the thumbnail thata" - [page-id {:keys [id x y width height] :as shape} node-ref rendered? thumbnail? disable?] + [page-id {:keys [id x y width height] :as shape} node-ref rendered? thumbnail-data-ref disable?] (let [frame-canvas-ref (mf/use-ref nil) frame-image-ref (mf/use-ref nil) @@ -58,8 +58,6 @@ shape-ref (hooks/use-update-var shape) - thumbnail-ref? (mf/use-var thumbnail?) - updates-str (mf/use-memo #(rx/subject)) on-image-load @@ -77,8 +75,12 @@ (fn [] (when (and (some? @node-ref) (not @disable-ref?)) (let [node @node-ref + frame-html (dom/node->xml node) {:keys [x y width height]} @shape-ref + + style-str (or (-> node dom/get-parent (dom/query "style") dom/node->xml) "") + svg-node (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") (dom/set-property! "version" "1.1") @@ -86,7 +88,7 @@ (dom/set-property! "width" width) (dom/set-property! "height" height) (dom/set-property! "fill" "none") - (obj/set! "innerHTML" frame-html)) + (obj/set! "innerHTML" (dm/str style-str frame-html))) img-src (-> svg-node dom/node->xml dom/svg->data-uri)] (reset! image-url img-src)))) @@ -94,7 +96,9 @@ (mf/use-callback (fn [node] (when (and (some? node) (nil? @observer-ref)) - (rx/push! updates-str :update) + (when-not (some? @thumbnail-data-ref) + (rx/push! updates-str :update)) + (let [observer (js/MutationObserver. (partial rx/push! updates-str))] (.observe observer node #js {:childList true :attributes true :characterData true :subtree true}) (reset! observer-ref observer)))))] @@ -102,7 +106,7 @@ (mf/use-effect (fn [] (let [subid (->> updates-str - (rx/debounce 200) + (rx/debounce 400) (rx/subs on-update-frame))] #(rx/dispose! subid)))) @@ -111,11 +115,6 @@ (fn [] (reset! disable-ref? disable?))) - (mf/use-effect - (mf/deps thumbnail?) - (fn [] - (reset! thumbnail-ref? thumbnail?))) - (mf/use-effect (fn [] #(when (and (some? @node-ref) @rendered?) diff --git a/frontend/src/app/util/dom.cljs b/frontend/src/app/util/dom.cljs index e6fe6cbec..dc25d62b3 100644 --- a/frontend/src/app/util/dom.cljs +++ b/frontend/src/app/util/dom.cljs @@ -335,8 +335,9 @@ (defn node->xml [node] - (-> (js/XMLSerializer.) - (.serializeToString node))) + (when (some? node) + (-> (js/XMLSerializer.) + (.serializeToString node)))) (defn svg->data-uri [svg] From edce45095e5e4dd18845986a772a9df919de9314 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 13:58:12 +0200 Subject: [PATCH 78/98] :bug: Remove xlinkHref to resolve Safari problem --- frontend/src/app/main/data/workspace/svg_upload.cljs | 4 ++-- frontend/src/app/main/ui/icons.clj | 2 +- frontend/src/app/main/ui/shapes/custom_stroke.cljs | 12 ++++++------ frontend/src/app/main/ui/shapes/fills.cljs | 2 +- frontend/src/app/main/ui/shapes/frame.cljs | 2 +- .../ui/workspace/shapes/frame/thumbnail_render.cljs | 2 +- .../src/app/main/ui/workspace/viewport/widgets.cljs | 2 +- frontend/src/app/render.cljs | 2 +- frontend/src/app/util/svg.cljs | 2 +- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/frontend/src/app/main/data/workspace/svg_upload.cljs b/frontend/src/app/main/data/workspace/svg_upload.cljs index df39c8bc2..40d9d14ee 100644 --- a/frontend/src/app/main/data/workspace/svg_upload.cljs +++ b/frontend/src/app/main/data/workspace/svg_upload.cljs @@ -326,7 +326,7 @@ transform (->> svg-transform (gmt/transform-in (gpt/point svg-data))) - image-url (:xlink:href attrs) + image-url (or (:href attrs) (:xlink:href attrs)) image-data (get-in svg-data [:image-data image-url]) rect (->> (select-keys attrs [:x :y :width :height]) @@ -352,7 +352,7 @@ (merge rect-metadata) (assoc :svg-viewbox (select-keys rect [:x :y :width :height])) - (assoc :svg-attrs (dissoc attrs :x :y :width :height :xlink:href)))))) + (assoc :svg-attrs (dissoc attrs :x :y :width :height :href :xlink:href)))))) (defn parse-svg-element [frame-id svg-data element-data unames] (let [{:keys [tag attrs]} element-data diff --git a/frontend/src/app/main/ui/icons.clj b/frontend/src/app/main/ui/icons.clj index a31a1fa92..e78929591 100644 --- a/frontend/src/app/main/ui/icons.clj +++ b/frontend/src/app/main/ui/icons.clj @@ -13,5 +13,5 @@ class (str "icon-" (name id))] `(rumext.alpha/html [:svg {:width 500 :height 500 :class ~class} - [:use {:xlinkHref ~href}]]))) + [:use {:href ~href}]]))) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index b125f8d45..ba4776f2b 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -34,7 +34,7 @@ clip-id (str "inner-stroke-" render-id "-" (:id shape) suffix) shape-id (str "stroke-shape-" render-id "-" (:id shape) suffix)] [:> "clipPath" #js {:id clip-id} - [:use {:xlinkHref (str "#" shape-id)}]])) + [:use {:href (str "#" shape-id)}]])) (mf/defc outer-stroke-mask [{:keys [shape render-id index]}] @@ -58,10 +58,10 @@ :width (:width bounding-box) :height (:height bounding-box) :maskUnits "userSpaceOnUse"} - [:use {:xlinkHref (str "#" shape-id) + [:use {:href (str "#" shape-id) :style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}] - [:use {:xlinkHref (str "#" shape-id) + [:use {:href (str "#" shape-id) :style #js {:fill "black" :stroke "none"}}]])) @@ -233,7 +233,7 @@ (obj/clone) (obj/without ["fill" "fillOpacity" "stroke" "strokeWidth" "strokeOpacity" "strokeStyle" "strokeDasharray"]))))]] - [:use {:xlinkHref (str "#" shape-id) + [:use {:href (str "#" shape-id) :mask (str "url(#" stroke-mask-id ")") :style (-> (obj/get base-props "style") (obj/clone) @@ -241,7 +241,7 @@ (obj/without ["fill" "fillOpacity"]) (obj/set! "fill" "none"))}] - [:use {:xlinkHref (str "#" shape-id) + [:use {:href (str "#" shape-id) :style (-> (obj/get base-props "style") (obj/clone) (obj/set! "stroke" "none"))}]])) @@ -277,7 +277,7 @@ [:& stroke-defs {:shape shape :render-id render-id :index index}] [:> elem-name shape-props]] - [:use {:xlinkHref (str "#" shape-id) + [:use {:href (str "#" shape-id) :clipPath clip-path}]])) ; The SVG standard does not implement yet the 'stroke-alignment' diff --git a/frontend/src/app/main/ui/shapes/fills.cljs b/frontend/src/app/main/ui/shapes/fills.cljs index 45ba0586e..a3a8769e7 100644 --- a/frontend/src/app/main/ui/shapes/fills.cljs +++ b/frontend/src/app/main/ui/shapes/fills.cljs @@ -79,7 +79,7 @@ (obj/set! "height" height))]) (when has-image? - [:image {:xlinkHref (get embed uri uri) + [:image {:href (get embed uri uri) :preserveAspectRatio "none" :width width :height height}])]])]))))) diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 168e26893..8e4d07866 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -68,7 +68,7 @@ [:image.frame-thumbnail {:id (dm/str "thumbnail-" (:id shape)) - :xlinkHref (:thumbnail shape) + :href (:thumbnail shape) :x (:x shape) :y (:y shape) :width (:width shape) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 002926e8d..0630fe201 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -137,7 +137,7 @@ [:image {:ref frame-image-ref :x (:x shape) :y (:y shape) - :xlinkHref @image-url + :href @image-url :width (:width shape) :height (:height shape) :on-load on-image-load}]]))])) diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index 1c22c42d1..75cbaa64d 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -147,7 +147,7 @@ :class "workspace-frame-icon" :style {:fill (when selected? "var(--color-primary-dark)")} :visibility (if show-artboard-names? "visible" "hidden")} - [:use {:xlinkHref "#icon-set-thumbnail"}]]]) + [:use {:href "#icon-set-thumbnail"}]]]) [:text {:x text-pos-x :y 0 :width width diff --git a/frontend/src/app/render.cljs b/frontend/src/app/render.cljs index 74280552c..5d921f0a7 100644 --- a/frontend/src/app/render.cljs +++ b/frontend/src/app/render.cljs @@ -264,7 +264,7 @@ :embed embed} (when-let [component-id (:component-id @state)] - [:use {:x 0 :y 0 :xlinkHref (str "#" component-id)}])]] + [:use {:x 0 :y 0 :href (str "#" component-id)}])]] ]))) diff --git a/frontend/src/app/util/svg.cljs b/frontend/src/app/util/svg.cljs index 198fd91c2..0fef4753e 100644 --- a/frontend/src/app/util/svg.cljs +++ b/frontend/src/app/util/svg.cljs @@ -961,5 +961,5 @@ (let [redfn (fn [acc {:keys [tag attrs]}] (cond-> acc (= :image tag) - (conj (:xlink:href attrs))))] + (conj (or (:href attrs) (:xlink:href attrs)))))] (reduce-nodes redfn [] svg-data ))) From 01664a04fc924645984b35b39f94c201a32fa22e Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 14:08:40 +0200 Subject: [PATCH 79/98] :bug: Problem recalculating thumbnails --- frontend/src/app/main/ui/shapes/fills.cljs | 6 +----- .../app/main/ui/workspace/shapes/frame.cljs | 4 +++- .../shapes/frame/thumbnail_render.cljs | 20 ++++++++++++++++--- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/fills.cljs b/frontend/src/app/main/ui/shapes/fills.cljs index a3a8769e7..2aee6e122 100644 --- a/frontend/src/app/main/ui/shapes/fills.cljs +++ b/frontend/src/app/main/ui/shapes/fills.cljs @@ -43,15 +43,11 @@ embed (embed/use-data-uris [uri]) transform (gsh/transform-matrix shape) - ;; When true the image has not loaded yet - loading? (and (some? uri) (not (contains? embed uri))) - pattern-attrs (cond-> #js {:patternUnits "userSpaceOnUse" :x x :y y :height height - :width width - :data-loading loading?} + :width width} (= :path (:type shape)) (obj/set! "patternTransform" transform))] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame.cljs b/frontend/src/app/main/ui/workspace/shapes/frame.cljs index 782600633..522c17258 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame.cljs @@ -126,7 +126,9 @@ (when (not @rendered?) (reset! rendered? true))))) [:& (mf/provider ctx/render-ctx) {:value render-id} - [:g.frame-container {:key "frame-container" :ref on-frame-load} + [:g.frame-container {:id (dm/str "frame-container-" (:id shape)) + :key "frame-container" + :ref on-frame-load} [:& ff/fontfaces-style {:fonts fonts}] [:g.frame-thumbnail-wrapper {:id (dm/str "thumbnail-container-" (:id shape))} [:> frame/frame-thumbnail {:key (dm/str (:id shape)) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 0630fe201..3280108a1 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -6,6 +6,7 @@ (ns app.main.ui.workspace.shapes.frame.thumbnail-render (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.math :as mth] [app.main.data.workspace :as dw] @@ -15,6 +16,7 @@ [app.util.object :as obj] [app.util.timers :as ts] [beicon.core :as rx] + [cuerdas.core :as str] [rumext.alpha :as mf])) ;; (def thumbnail-scale-factor 2) @@ -41,6 +43,16 @@ (.error js/console err) nil))) +(defn- remove-embed-images-changes + "Remove the changes related to change a url for its embed value. This is necessary + so we don't have to recalculate the thumbnail when the image loads." + [changes] + (->> changes + (remove (fn [change] + (and (= "attributes" (.-type change)) + (= "href" (.-attributeName change)) + (str/starts-with? (.-oldValue change) "http")))))) + (defn use-render-thumbnail "Hook that will create the thumbnail thata" [page-id {:keys [id x y width height] :as shape} node-ref rendered? thumbnail-data-ref disable?] @@ -75,11 +87,11 @@ (fn [] (when (and (some? @node-ref) (not @disable-ref?)) (let [node @node-ref - frame-html (dom/node->xml node) {:keys [x y width height]} @shape-ref - style-str (or (-> node dom/get-parent (dom/query "style") dom/node->xml) "") + style-node (dom/query (dm/str "#frame-container-" (:id shape) " style")) + style-str (or (-> style-node dom/node->xml) "") svg-node (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") @@ -100,12 +112,14 @@ (rx/push! updates-str :update)) (let [observer (js/MutationObserver. (partial rx/push! updates-str))] - (.observe observer node #js {:childList true :attributes true :characterData true :subtree true}) + (.observe observer node #js {:childList true :attributes true :attributeOldValue true :characterData true :subtree true}) (reset! observer-ref observer)))))] (mf/use-effect (fn [] (let [subid (->> updates-str + (rx/map remove-embed-images-changes) + (rx/filter d/not-empty?) (rx/debounce 400) (rx/subs on-update-frame))] #(rx/dispose! subid)))) From 95a2da5ebc706b44cbcf451a68da8e884e823056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 12 May 2022 14:10:31 +0200 Subject: [PATCH 80/98] :sparkles: Rework multi edit of measures values --- common/src/app/common/attrs.cljc | 77 +++++++++++++------ common/src/app/common/data.cljc | 10 +++ common/src/app/common/geom/shapes/text.cljc | 1 - .../app/common/geom/shapes/transforms.cljc | 3 +- common/src/app/common/pages/common.cljc | 9 +++ .../src/app/main/data/workspace/texts.cljs | 6 +- .../workspace/shapes/text/viewport_texts.cljs | 3 +- .../sidebar/options/menus/measures.cljs | 27 +++++-- 8 files changed, 100 insertions(+), 36 deletions(-) diff --git a/common/src/app/common/attrs.cljc b/common/src/app/common/attrs.cljc index febb78446..b63f036d8 100644 --- a/common/src/app/common/attrs.cljc +++ b/common/src/app/common/attrs.cljc @@ -5,7 +5,49 @@ ;; Copyright (c) UXBOX Labs SL (ns app.common.attrs - (:refer-clojure :exclude [merge])) + (:require + [app.common.geom.shapes.transforms :as gst] + [app.common.math :as mth])) + +(defn- get-attr + [obj attr] + (if (= (get obj attr) :multiple) + :multiple + (cond + ;; For rotated or stretched shapes, the origin point we show in the menu + ;; is not the (:x :y) shape attribute, but the top left coordinate of the + ;; wrapping recangle (see measures.cljs). As the :points attribute cannot + ;; be merged for several objects, we calculate the origin point in two fake + ;; attributes to be used in the measures menu. + (#{:ox :oy} attr) + (if-let [value (get obj attr)] + value + (if-let [points (:points obj)] + (if (not= points :multiple) + (let [rect (gst/selection-rect [obj])] + (if (= attr :ox) (:x rect) (:y rect))) + :multiple) + (get obj attr ::unset))) + + ;; Not all shapes have width and height (e.g. paths), so we extract + ;; them from the :selrect attribute. + (#{:width :height} attr) + (if-let [value (get obj attr)] + value + (if-let [selrect (:selrect obj)] + (if (not= selrect :multiple) + (get (:selrect obj) attr) + :multiple) + (get obj attr ::unset))) + + :else + (get obj attr ::unset)))) + +(defn- default-equal + [val1 val2] + (if (and (number? val1) (number? val2)) + (mth/close? val1 val2) + (= val1 val2))) ;; Extract some attributes of a list of shapes. ;; For each attribute, if the value is the same in all shapes, @@ -36,13 +78,11 @@ ;; :rx nil ;; :ry nil} ;; - (defn get-attrs-multi ([objs attrs] - (get-attrs-multi objs attrs = identity)) + (get-attrs-multi objs attrs default-equal identity)) ([objs attrs eqfn sel] - (loop [attr (first attrs) attrs (rest attrs) result (transient {})] @@ -50,34 +90,25 @@ (let [value (loop [curr (first objs) objs (rest objs) - value ::undefined] + value ::unset] (if (and curr (not= value :multiple)) - ;; - (let [new-val (get curr attr ::undefined) + (let [new-val (get-attr curr attr) value (cond - (= new-val ::undefined) value - (= new-val :multiple) :multiple - (= value ::undefined) (sel new-val) - (eqfn new-val value) value - :else :multiple)] + (= new-val ::unset) value + (= new-val :multiple) :multiple + (= value ::unset) (sel new-val) + (eqfn new-val value) value + :else :multiple)] (recur (first objs) (rest objs) value)) - ;; + value))] + (recur (first attrs) (rest attrs) (cond-> result - (not= value ::undefined) + (not= value ::unset) (assoc! attr value)))) (persistent! result))))) -(defn merge - "Attrs specific merge function." - [obj attrs] - (reduce-kv (fn [obj k v] - (if (nil? v) - (dissoc obj k) - (assoc obj k v))) - obj - attrs)) diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index b7603c8ed..ee7bd560e 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -336,6 +336,16 @@ [& maps] (reduce conj (or (first maps) {}) (rest maps))) +(defn txt-merge + "Text attrs specific merge function." + [obj attrs] + (reduce-kv (fn [obj k v] + (if (nil? v) + (dissoc obj k) + (assoc obj k v))) + obj + attrs)) + (defn distinct-xf [f] (fn [rf] diff --git a/common/src/app/common/geom/shapes/text.cljc b/common/src/app/common/geom/shapes/text.cljc index a23cca61a..9fdb0b34b 100644 --- a/common/src/app/common/geom/shapes/text.cljc +++ b/common/src/app/common/geom/shapes/text.cljc @@ -28,4 +28,3 @@ [shape] (gpr/points->selrect (position-data-points shape))) - diff --git a/common/src/app/common/geom/shapes/transforms.cljc b/common/src/app/common/geom/shapes/transforms.cljc index 3f561b602..cd0a707b6 100644 --- a/common/src/app/common/geom/shapes/transforms.cljc +++ b/common/src/app/common/geom/shapes/transforms.cljc @@ -6,7 +6,6 @@ (ns app.common.geom.shapes.transforms (:require - [app.common.attrs :as attrs] [app.common.data :as d] [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] @@ -533,7 +532,7 @@ (* (get-in modifiers [:resize-vector :x] 1)) (* (get-in modifiers [:resize-vector-2 :x] 1)) (str))] - (attrs/merge attrs {:font-size font-size})))] + (d/txt-merge attrs {:font-size font-size})))] (update shape :content #(txt/transform-nodes txt/is-text-node? merge-attrs diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index b7f6f2280..2ac202d73 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -79,6 +79,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :opacity :blend-mode @@ -112,6 +113,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -137,6 +139,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -179,6 +182,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -221,6 +225,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -263,6 +268,7 @@ :x :y :rotation :selrect + :points :constraints-h :constraints-v @@ -330,6 +336,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -355,6 +362,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v @@ -399,6 +407,7 @@ :rx :ry :r1 :r2 :r3 :r4 :selrect + :points :constraints-h :constraints-v diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index b9545818f..ec0045125 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -187,8 +187,8 @@ update-fn (fn [shape] (if (some? (:content shape)) - (update-text-content shape txt/is-root-node? attrs/merge attrs) - (assoc shape :content (attrs/merge {:type "root"} attrs)))) + (update-text-content shape txt/is-root-node? d/txt-merge attrs) + (assoc shape :content (d/txt-merge {:type "root"} attrs)))) shape-ids (cond (cph/text-shape? shape) [id] (cph/group-shape? shape) (cph/get-children-ids objects id))] @@ -240,7 +240,7 @@ shape-ids (cond (cph/text-shape? shape) [id] (cph/group-shape? shape) (cph/get-children-ids objects id))] - (rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? attrs/merge attrs)))))))) + (rx/of (dch/update-shapes shape-ids #(update-text-content % update-node? d/txt-merge attrs)))))))) (defn migrate-node [node] diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index 2e65fd79c..b78c3967a 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -6,7 +6,6 @@ (ns app.main.ui.workspace.shapes.text.viewport-texts (:require - [app.common.attrs :as attrs] [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] @@ -50,7 +49,7 @@ (cond-> shape (and (some? shape) (some? editor-content)) - (assoc :content (attrs/merge content editor-content))))) + (assoc :content (d/txt-merge content editor-content))))) (defn- update-text-shape [{:keys [grow-type id]} node] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs index dfc8f1640..58d4bc2d4 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/measures.cljs @@ -25,10 +25,12 @@ [:proportion-lock :width :height :x :y + :ox :oy :rotation :rx :ry :r1 :r2 :r3 :r4 - :selrect]) + :selrect + :points]) (def ^:private type->options {:bool #{:size :position :rotation} @@ -46,7 +48,7 @@ ;; -- User/drawing coords (mf/defc measures-menu [{:keys [ids ids-with-children values type all-types shape] :as props}] - + (let [options (if (= type :multiple) (reduce #(union %1 %2) (map #(get type->options %) all-types)) (get type->options type)) @@ -58,20 +60,36 @@ [shape]) frames (map #(deref (refs/object-by-id (:frame-id %))) old-shapes) + ;; To show interactively the measures while the user is manipulating + ;; the shape with the mouse, generate a copy of the shapes applying + ;; the transient tranformations. shapes (as-> old-shapes $ (map gsh/transform-shape $) (map gsh/translate-to-frame $ frames)) - values (let [{:keys [x y]} (-> shapes first :points gsh/points->selrect)] + ;; For rotated or stretched shapes, the origin point we show in the menu + ;; is not the (:x :y) shape attribute, but the top left coordinate of the + ;; wrapping rectangle. + values (let [{:keys [x y]} (gsh/selection-rect [(first shapes)])] (cond-> values (not= (:x values) :multiple) (assoc :x x) - (not= (:y values) :multiple) (assoc :y y))) + (not= (:y values) :multiple) (assoc :y y) + ;; In case of multiple selection, the origin point has been already + ;; calculated and given in the fake :ox and :oy attributes. See + ;; common/src/app/common/attrs.cljc + (some? (:ox values)) (assoc :x (:ox values)) + (some? (:oy values)) (assoc :y (:oy values)))) + ;; For :height and :width we take those in the :selrect attribute, because + ;; not all shapes have an own :width and :height (e. g. paths). Here the + ;; rotation is ignored (selrect always has the original size excluding + ;; transforms). values (let [{:keys [width height]} (-> shapes first :selrect)] (cond-> values (not= (:width values) :multiple) (assoc :width width) (not= (:height values) :multiple) (assoc :height height))) + ;; The :rotation, however, does use the transforms. values (let [{:keys [rotation] :or {rotation 0}} (-> shapes first)] (cond-> values (not= (:rotation values) :multiple) (assoc :rotation rotation))) @@ -85,7 +103,6 @@ radius-multi? (mf/use-state nil) radius-input-ref (mf/use-ref nil) - on-preset-selected (fn [width height] (st/emit! (udw/update-dimensions ids :width width) From eabfa7a54194e4a7c76c670b81f42ef4bbef96b3 Mon Sep 17 00:00:00 2001 From: Eva Date: Tue, 17 May 2022 16:38:24 +0200 Subject: [PATCH 81/98] :bug: Fix scroll into view with big groups --- frontend/src/app/main/ui/workspace/sidebar/layers.cljs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index b76e9b7d8..e13502999 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -32,10 +32,9 @@ (l/derived (l/in [:workspace-local :shape-for-rename]) st/state)) (mf/defc layer-name - [{:keys [shape on-start-edit on-stop-edit] :as props}] + [{:keys [shape on-start-edit on-stop-edit name-ref] :as props}] (let [local (mf/use-state {}) shape-for-rename (mf/deref shape-for-rename-ref) - name-ref (mf/use-ref) start-edit (fn [] (on-start-edit) @@ -179,13 +178,15 @@ :detect-center? container? :data {:id (:id item) :index index - :name (:name item)})] + :name (:name item)}) + + ref (mf/use-ref)] (mf/use-effect (mf/deps selected? selected) (fn [] (let [single? (= (count selected) 1) - node (mf/ref-val dref) + node (mf/ref-val ref) subid (when (and single? selected?) @@ -218,6 +219,7 @@ :on-double-click #(dom/stop-propagation %)} [:& si/element-icon {:shape item}] [:& layer-name {:shape item + :name-ref ref :on-start-edit #(reset! disable-drag true) :on-stop-edit #(reset! disable-drag false)}] From c9dadce12aae5fbb6a650d6a3c6eb60a0b95c583 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 17:02:11 +0200 Subject: [PATCH 82/98] :zap: Skip calculate children modifiers on move --- .../app/common/geom/shapes/constraints.cljc | 73 ++++++++++--------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/common/src/app/common/geom/shapes/constraints.cljc b/common/src/app/common/geom/shapes/constraints.cljc index 32064b22d..1565392f8 100644 --- a/common/src/app/common/geom/shapes/constraints.cljc +++ b/common/src/app/common/geom/shapes/constraints.cljc @@ -176,45 +176,50 @@ (defn calc-child-modifiers [parent child modifiers ignore-constraints transformed-parent-rect] - (let [constraints-h - (if-not ignore-constraints - (:constraints-h child (default-constraints-h child)) - :scale) - constraints-v - (if-not ignore-constraints - (:constraints-v child (default-constraints-v child)) - :scale) + (if (and (nil? (:resize-vector modifiers)) + (nil? (:resize-vector-2 modifiers))) + ;; If we don't have a resize modifier we return the same modifiers + modifiers + (let [constraints-h + (if-not ignore-constraints + (:constraints-h child (default-constraints-h child)) + :scale) - modifiers-h (constraint-modifier (constraints-h const->type+axis) :x parent child modifiers transformed-parent-rect) - modifiers-v (constraint-modifier (constraints-v const->type+axis) :y parent child modifiers transformed-parent-rect)] + constraints-v + (if-not ignore-constraints + (:constraints-v child (default-constraints-v child)) + :scale) - ;; Build final child modifiers. Apply transform again to the result, to get the - ;; real modifiers that need to be applied to the child, including rotation as needed. - (cond-> {} - (or (contains? modifiers-h :displacement) - (contains? modifiers-v :displacement)) - (assoc :displacement (cond-> (gpt/point (get-in modifiers-h [:displacement :x] 0) - (get-in modifiers-v [:displacement :y] 0)) - (some? (:resize-transform modifiers)) - (gpt/transform (:resize-transform modifiers)) + modifiers-h (constraint-modifier (constraints-h const->type+axis) :x parent child modifiers transformed-parent-rect) + modifiers-v (constraint-modifier (constraints-v const->type+axis) :y parent child modifiers transformed-parent-rect)] - :always - (gmt/translate-matrix))) + ;; Build final child modifiers. Apply transform again to the result, to get the + ;; real modifiers that need to be applied to the child, including rotation as needed. + (cond-> {} + (or (contains? modifiers-h :displacement) + (contains? modifiers-v :displacement)) + (assoc :displacement (cond-> (gpt/point (get-in modifiers-h [:displacement :x] 0) + (get-in modifiers-v [:displacement :y] 0)) + (some? (:resize-transform modifiers)) + (gpt/transform (:resize-transform modifiers)) - (:resize-vector modifiers-h) - (assoc :resize-origin (:resize-origin modifiers-h) - :resize-vector (gpt/point (get-in modifiers-h [:resize-vector :x] 1) - (get-in modifiers-h [:resize-vector :y] 1))) + :always + (gmt/translate-matrix))) - (:resize-vector modifiers-v) - (assoc :resize-origin-2 (:resize-origin modifiers-v) - :resize-vector-2 (gpt/point (get-in modifiers-v [:resize-vector :x] 1) - (get-in modifiers-v [:resize-vector :y] 1))) + (:resize-vector modifiers-h) + (assoc :resize-origin (:resize-origin modifiers-h) + :resize-vector (gpt/point (get-in modifiers-h [:resize-vector :x] 1) + (get-in modifiers-h [:resize-vector :y] 1))) - (:resize-transform modifiers) - (assoc :resize-transform (:resize-transform modifiers) - :resize-transform-inverse (:resize-transform-inverse modifiers)) + (:resize-vector modifiers-v) + (assoc :resize-origin-2 (:resize-origin modifiers-v) + :resize-vector-2 (gpt/point (get-in modifiers-v [:resize-vector :x] 1) + (get-in modifiers-v [:resize-vector :y] 1))) - :always - (clean-modifiers)))) + (:resize-transform modifiers) + (assoc :resize-transform (:resize-transform modifiers) + :resize-transform-inverse (:resize-transform-inverse modifiers)) + + :always + (clean-modifiers))))) From 9ab922a0faadea518e7c615fdd104dbc7861a4af Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 17:02:28 +0200 Subject: [PATCH 83/98] :zap: Improved snap-pixel performance --- .../src/app/main/data/workspace/transforms.cljs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 058f1b42c..6b2cc9d22 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -356,22 +356,27 @@ [modif-tree shape modifiers] (let [children (map (d/getf objects) (:shapes shape)) - modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape)) transformed-rect (gsh/transform-selrect (:selrect shape) modifiers) set-child - (fn [modif-tree child] - (let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect)] + (fn [snap-pixel? modif-tree child] + (let [child-modifiers (gsh/calc-child-modifiers shape child modifiers ignore-constraints transformed-rect) + child-modifiers (cond-> child-modifiers snap-pixel? (set-pixel-precision child))] (cond-> modif-tree (not (gsh/empty-modifiers? child-modifiers)) (set-modifiers-rec child child-modifiers)))) modif-tree (-> modif-tree - (assoc-in [(:id shape) :modifiers] modifiers))] + (assoc-in [(:id shape) :modifiers] modifiers)) - (reduce set-child modif-tree children)))] - (set-modifiers-rec modif-tree shape modifiers))) + resize-modif? + (or (:resize-vector modifiers) (:resize-vector-2 modifiers))] + + (reduce (partial set-child (and snap-pixel? resize-modif?)) modif-tree children)))] + + (let [modifiers (cond-> modifiers snap-pixel? (set-pixel-precision shape))] + (set-modifiers-rec modif-tree shape modifiers)))) (defn- get-ignore-tree "Retrieves a map with the flag `ignore-geometry?` given a tree of modifiers" From a8cc9ace725e5f33fee6323705e2abfa43fcc766 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 17 May 2022 17:02:45 +0200 Subject: [PATCH 84/98] :zap: Improved text move performance --- .../workspace/shapes/text/viewport_texts.cljs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index b78c3967a..f799bf63b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -28,9 +28,12 @@ (defn strip-position-data [shape] (dissoc shape :position-data :transform :transform-inverse)) +(defn strip-modifier + [modifier] + (d/update-when modifier :modifiers dissoc :displacement :rotation)) + (defn process-shape [modifiers {:keys [id] :as shape}] - (let [modifier (get modifiers id) - modifier (d/update-when modifier :modifiers dissoc :displacement :rotation) + (let [modifier (-> (get modifiers id) strip-modifier) shape (cond-> shape (not (gsh/empty-modifiers? (:modifiers modifier))) (-> (assoc :grow-type :fixed) @@ -117,11 +120,16 @@ text-change? (fn [id] (let [old-shape (get prev-text-shapes id) - new-shape (get text-shapes id)] + new-shape (get text-shapes id) + old-modifiers (-> (get prev-modifiers id) strip-modifier) + new-modifiers (-> (get modifiers id) strip-modifier)] (or (and (not (identical? old-shape new-shape)) (not= old-shape new-shape)) - (not= (get modifiers id) - (get prev-modifiers id))))) + + ;; The shape has changed only if its modifier is not empty and it's different + (and (not= new-modifiers old-modifiers) + (or (not (gsh/empty-modifiers? (:modifiers old-modifiers))) + (not (gsh/empty-modifiers? (:modifiers new-modifiers)))))))) changed-texts (mf/use-memo From 919ca68a775b7d5474b4aefa344323d95d1e4fd3 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Wed, 18 May 2022 10:35:11 +0200 Subject: [PATCH 85/98] :bug: Fix undo when drawing curve --- frontend/src/app/main/data/workspace/common.cljs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/common.cljs b/frontend/src/app/main/data/workspace/common.cljs index 187ee5c44..f396d8547 100644 --- a/frontend/src/app/main/data/workspace/common.cljs +++ b/frontend/src/app/main/data/workspace/common.cljs @@ -124,7 +124,7 @@ (let [edition (get-in state [:workspace-local :edition]) drawing (get state :workspace-drawing)] ;; Editors handle their own undo's - (when-not (or (some? edition) (not-empty drawing)) + (when-not (or (some? edition) (and (not-empty drawing) (nil? (:object drawing)))) (let [undo (:workspace-undo state) items (:items undo) index (or (:index undo) (dec (count items)))] From d666755159c205f7b70ca4c763e0d163c9dd29ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Tue, 17 May 2022 09:59:09 +0200 Subject: [PATCH 86/98] :bug: Synchronize text positions in components --- common/src/app/common/geom/point.cljc | 10 ++++- common/src/app/common/geom/shapes.cljc | 38 +++++++++++++++++++ common/src/app/common/pages/changes.cljc | 33 ++++++++++------ common/src/app/common/pages/common.cljc | 2 + .../app/main/data/workspace/transforms.cljs | 18 ++++++--- .../workspace/shapes/text/viewport_texts.cljs | 1 - 6 files changed, 84 insertions(+), 18 deletions(-) diff --git a/common/src/app/common/geom/point.cljc b/common/src/app/common/geom/point.cljc index 9472fdce3..7c82ee0c9 100644 --- a/common/src/app/common/geom/point.cljc +++ b/common/src/app/common/geom/point.cljc @@ -100,7 +100,6 @@ (assert (point? other)) (Point. (/ x ox) (/ y oy))) - (defn min ([] (min nil nil)) ([p1] (min p1 nil)) @@ -139,6 +138,15 @@ (mth/sqrt (+ (mth/pow dx 2) (mth/pow dy 2))))) +(defn distance-vector + "Calculate the distance, separated x and y." + [{x :x y :y :as p} {ox :x oy :y :as other}] + (assert (point? p)) + (assert (point? other)) + (let [dx (mth/abs (- x ox)) + dy (mth/abs (- y oy))] + (Point. dx dy))) + (defn length [{x :x y :y :as p}] (assert (point? p)) diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index e201d3181..ac645e920 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -6,6 +6,7 @@ (ns app.common.geom.shapes (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes.bool :as gsb] @@ -38,6 +39,14 @@ ;; --- Helpers +(defn left-bound + [shape] + (get shape :x (:x (:selrect shape)))) ; Paths don't have :x attribute + +(defn top-bound + [shape] + (get shape :y (:y (:selrect shape)))) ; Paths don't have :y attribute + (defn fully-contained? "Checks if one rect is fully inside the other" [rect other] @@ -96,6 +105,35 @@ (mth/sqrt (* 2 stroke-width stroke-width)) (- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width))) +(defn close-attrs? + "Compares two shapes attributes to see if they are equal or almost + equal (in case of numeric). Takes into account attributes that are + data structures with numbers inside." + ([attr val1 val2] + (close-attrs? attr val1 val2 mth/float-equal-precision)) + + ([attr val1 val2 precision] + (let [close-val? (fn [num1 num2] (< (mth/abs (- num1 num2)) precision))] + (cond + (and (number? val1) (number? val2)) + (close-val? val1 val2) + + (= attr :selrect) + (every? #(close-val? (get val1 %) (get val2 %)) + [:x :y :x1 :y1 :x2 :y2 :width :height]) + + (= attr :points) + (every? #(and (close-val? (:x (first %)) (:x (second %))) + (close-val? (:y (first %)) (:y (second %)))) + (d/zip val1 val2)) + + (= attr :position-data) + (every? #(and (close-val? (:x (first %)) (:x (second %))) + (close-val? (:y (first %)) (:y (second %)))) + (d/zip val1 val2)) + + :else + (= val1 val2))))) ;; EXPORTS (dm/export gco/center-shape) diff --git a/common/src/app/common/pages/changes.cljc b/common/src/app/common/pages/changes.cljc index 8ca215bde..7d511d9ff 100644 --- a/common/src/app/common/pages/changes.cljc +++ b/common/src/app/common/pages/changes.cljc @@ -11,6 +11,7 @@ [app.common.exceptions :as ex] [app.common.geom.shapes :as gsh] [app.common.geom.shapes.bool :as gshb] + [app.common.math :as mth] [app.common.pages.common :refer [component-sync-attrs]] [app.common.pages.helpers :as cph] [app.common.pages.init :as init] @@ -433,25 +434,35 @@ (defmethod process-operation :set [shape op] - (let [attr (:attr op) - val (:val op) - ignore (:ignore-touched op) + (let [attr (:attr op) + group (get component-sync-attrs attr) + val (:val op) + shape-val (get shape attr) + ignore (:ignore-touched op) ignore-geometry (:ignore-geometry op) - shape-ref (:shape-ref shape) - group (get component-sync-attrs attr) - root-name? (and (= group :name-group) - (:component-root? shape))] + is-geometry? (and (or (= group :geometry-group) + (and (= group :content-group) (= (:type shape) :path))) + (not (#{:width :height} attr))) ;; :content in paths are also considered geometric + shape-ref (:shape-ref shape) + root-name? (and (= group :name-group) + (:component-root? shape)) + + ;; For geometric attributes, there are cases in that the value changes + ;; slightly (e.g. when rounding to pixel, or when recalculating text + ;; positions in different zoom levels). To take this into account, we + ;; ignore geometric changes smaller than 1 pixel. + equal? (if is-geometry? + (gsh/close-attrs? attr val shape-val 1) + (gsh/close-attrs? attr val shape-val))] (cond-> shape ;; Depending on the origin of the attribute change, we need or not to ;; set the "touched" flag for the group the attribute belongs to. ;; In some cases we need to ignore touched only if the attribute is ;; geometric (position, width or transformation). - (and shape-ref group (not ignore) (not= val (get shape attr)) + (and shape-ref group (not ignore) (not equal?) (not root-name?) - (not (and ignore-geometry - (and (= group :geometry-group) - (not (#{:width :height} attr)))))) + (not (and ignore-geometry is-geometry?))) (-> (update :touched cph/set-touched-group group) (dissoc :remote-synced?)) diff --git a/common/src/app/common/pages/common.cljc b/common/src/app/common/pages/common.cljc index 2ac202d73..30351a666 100644 --- a/common/src/app/common/pages/common.cljc +++ b/common/src/app/common/pages/common.cljc @@ -59,8 +59,10 @@ :y :geometry-group :width :geometry-group :height :geometry-group + :rotation :geometry-group :transform :geometry-group :transform-inverse :geometry-group + :position-data :geometry-group :opacity :layer-effects-group :blend-mode :layer-effects-group :shadow :shadow-group diff --git a/frontend/src/app/main/data/workspace/transforms.cljs b/frontend/src/app/main/data/workspace/transforms.cljs index 6b2cc9d22..c73384c54 100644 --- a/frontend/src/app/main/data/workspace/transforms.cljs +++ b/frontend/src/app/main/data/workspace/transforms.cljs @@ -252,15 +252,23 @@ shape-delta (when root - (gpt/point (- (:x shape) (:x root)) - (- (:y shape) (:y root)))) + (gpt/point (- (gsh/left-bound shape) (gsh/left-bound root)) + (- (gsh/top-bound shape) (gsh/top-bound root)))) transformed-shape-delta (when transformed-root - (gpt/point (- (:x transformed-shape) (:x transformed-root)) - (- (:y transformed-shape) (:y transformed-root)))) + (gpt/point (- (gsh/left-bound transformed-shape) (gsh/left-bound transformed-root)) + (- (gsh/top-bound transformed-shape) (gsh/top-bound transformed-root)))) - ignore-geometry? (= shape-delta transformed-shape-delta)] + ;; There are cases in that the coordinates change slightly (e.g. when + ;; rounding to pixel, or when recalculating text positions in different + ;; zoom levels). To take this into account, we ignore movements smaller + ;; than 1 pixel. + distance (if (and shape-delta transformed-shape-delta) + (gpt/distance-vector shape-delta transformed-shape-delta) + (gpt/point 0 0)) + + ignore-geometry? (and (< (:x distance) 1) (< (:y distance) 1))] [root transformed-root ignore-geometry?])) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index f799bf63b..b8e726951 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -72,7 +72,6 @@ (defn- update-text-modifier [{:keys [grow-type id]} node] - (let [position-data (utp/calc-position-data node) props {:position-data position-data} From 6a74f29f96d7c3e9c5938214282b5b14e4fcdde1 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 18 May 2022 15:52:45 +0200 Subject: [PATCH 87/98] :bug: Fix problem when exporting penpot files --- common/src/app/common/data.cljc | 25 +++++++++++++----------- frontend/src/app/util/import/parser.cljs | 2 +- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/common/src/app/common/data.cljc b/common/src/app/common/data.cljc index ee7bd560e..874ee56b9 100644 --- a/common/src/app/common/data.cljc +++ b/common/src/app/common/data.cljc @@ -584,17 +584,20 @@ (assert (string? basename)) (assert (set? used)) - (let [[prefix initial] (extract-numeric-suffix basename)] - (if (and (not prefix-first?) - (not (contains? used basename))) - basename - (loop [counter initial] - (let [candidate (if (and (= 1 counter) prefix-first?) - (str prefix) - (str prefix "-" counter))] - (if (contains? used candidate) - (recur (inc counter)) - candidate))))))) + (if (> (count basename) 1000) + ;; We skip generating names for long strings. If the name is too long the regex can hang + basename + (let [[prefix initial] (extract-numeric-suffix basename)] + (if (and (not prefix-first?) + (not (contains? used basename))) + basename + (loop [counter initial] + (let [candidate (if (and (= 1 counter) prefix-first?) + (str prefix) + (str prefix "-" counter))] + (if (contains? used candidate) + (recur (inc counter)) + candidate)))))))) (defn deep-mapm "Applies a map function to an associative map and recurses over its children diff --git a/frontend/src/app/util/import/parser.cljs b/frontend/src/app/util/import/parser.cljs index 28767b265..ba6a191a6 100644 --- a/frontend/src/app/util/import/parser.cljs +++ b/frontend/src/app/util/import/parser.cljs @@ -811,7 +811,7 @@ :attrs) image-data (get-svg-data :image node) svg-data (or image-data pattern-data)] - (:xlink:href svg-data))) + (or (:href svg-data) (:xlink:href svg-data)))) (defn get-image-fill [node] From 2b1e126ff8950cbd971c335268d530c9c9194b38 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 18 May 2022 17:04:59 +0200 Subject: [PATCH 88/98] :bug: Fix problem with thumbnails --- .../workspace/shapes/frame/thumbnail_render.cljs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index 3280108a1..f6926234a 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -46,12 +46,14 @@ (defn- remove-embed-images-changes "Remove the changes related to change a url for its embed value. This is necessary so we don't have to recalculate the thumbnail when the image loads." - [changes] - (->> changes - (remove (fn [change] - (and (= "attributes" (.-type change)) - (= "href" (.-attributeName change)) - (str/starts-with? (.-oldValue change) "http")))))) + [value] + (if (.isArray js/Array value) + (->> value + (remove (fn [change] + (and (= "attributes" (.-type change)) + (= "href" (.-attributeName change)) + (str/starts-with? (.-oldValue change) "http"))))) + [value])) (defn use-render-thumbnail "Hook that will create the thumbnail thata" @@ -121,6 +123,7 @@ (rx/map remove-embed-images-changes) (rx/filter d/not-empty?) (rx/debounce 400) + (rx/catch (fn [err] (.error js/console err))) (rx/subs on-update-frame))] #(rx/dispose! subid)))) From b5e696c6b4574d88543698f42d994323318263cb Mon Sep 17 00:00:00 2001 From: Eva Date: Wed, 18 May 2022 17:03:51 +0200 Subject: [PATCH 89/98] :bug: Fix ungroup typographies on edit --- frontend/src/app/main/data/workspace/libraries.cljs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 285e85523..fafea09db 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -222,6 +222,9 @@ (watch [it state _] (let [data (get state :workspace-data) [path name] (cph/parse-path-name (:name typography)) + path (if (and (:path typography) (= "" path)) + (:path typography) + path) typography (assoc typography :path path :name name) changes (-> (pcb/empty-changes it) (pcb/with-library-data data) From e22ef536ed875cf0424d4f91498c0f3ea3b98d07 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Wed, 18 May 2022 22:27:21 +0200 Subject: [PATCH 90/98] :bug: Fix problem with Safari and text resize --- frontend/src/app/util/text_position_data.js | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/frontend/src/app/util/text_position_data.js b/frontend/src/app/util/text_position_data.js index 40a18b3ba..fb37db1a1 100644 --- a/frontend/src/app/util/text_position_data.js +++ b/frontend/src/app/util/text_position_data.js @@ -18,7 +18,7 @@ goog.scope(function () { const range = document.createRange(); range.setStart(node, start); range.setEnd(node, end); - return range.getClientRects(); + return [...range.getClientRects()].filter((r) => r.width > 0); } self.parse_text_nodes = function(parent, textNode) { @@ -31,10 +31,20 @@ goog.scope(function () { let result = []; let prevRect = null; + // This variable is to make sure there are not infinite loops + // when we don't advance `to` we store true and then force to + // advance `to` on the next iteration if the condition is true again + let safeguard = false; + while (to < textSize) { const rects = getRangeRects(textNode, from, to + 1); - if (rects.length > 1) { + if (rects.length > 1 && safeguard) { + from++; + to++; + safeguard = false; + + } else if (rects.length > 1) { const position = prevRect; result.push({ @@ -45,11 +55,12 @@ goog.scope(function () { from = to; current = ""; - + safeguard = true; } else { prevRect = rects[0]; current += content[to]; to = to + 1; + safeguard = false; } } From 30bcdda90e175f283f55709b937c7bd9d2e27e9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 19 May 2022 09:49:28 +0200 Subject: [PATCH 91/98] :bug: Add a protection for some corner cases --- common/src/app/common/geom/shapes.cljc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/common/src/app/common/geom/shapes.cljc b/common/src/app/common/geom/shapes.cljc index ac645e920..c9b919ba2 100644 --- a/common/src/app/common/geom/shapes.cljc +++ b/common/src/app/common/geom/shapes.cljc @@ -113,7 +113,9 @@ (close-attrs? attr val1 val2 mth/float-equal-precision)) ([attr val1 val2 precision] - (let [close-val? (fn [num1 num2] (< (mth/abs (- num1 num2)) precision))] + (let [close-val? (fn [num1 num2] + (when (and (number? num1) (number? num2)) + (< (mth/abs (- num1 num2)) precision)))] (cond (and (number? val1) (number? val2)) (close-val? val1 val2) From 25ff15c62e93a8de5ae88cdda261fcb9063f039c Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 19 May 2022 14:14:55 +0200 Subject: [PATCH 92/98] :bug: Fix rendering thumbnail with pending images/fonts --- frontend/src/app/main/ui/shapes/fills.cljs | 6 +- .../app/main/ui/shapes/text/fontfaces.cljs | 5 +- .../shapes/frame/thumbnail_render.cljs | 79 +++++++++++++------ 3 files changed, 63 insertions(+), 27 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/fills.cljs b/frontend/src/app/main/ui/shapes/fills.cljs index 2aee6e122..a3a8769e7 100644 --- a/frontend/src/app/main/ui/shapes/fills.cljs +++ b/frontend/src/app/main/ui/shapes/fills.cljs @@ -43,11 +43,15 @@ embed (embed/use-data-uris [uri]) transform (gsh/transform-matrix shape) + ;; When true the image has not loaded yet + loading? (and (some? uri) (not (contains? embed uri))) + pattern-attrs (cond-> #js {:patternUnits "userSpaceOnUse" :x x :y y :height height - :width width} + :width width + :data-loading loading?} (= :path (:type shape)) (obj/set! "patternTransform" transform))] diff --git a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs index d08ebbb87..a470d5238 100644 --- a/frontend/src/app/main/ui/shapes/text/fontfaces.cljs +++ b/frontend/src/app/main/ui/shapes/text/fontfaces.cljs @@ -64,14 +64,17 @@ (mf/deps fonts-css) #(fonts/extract-fontface-urls fonts-css)) + ;; Calculate the data-uris for these fonts fonts-embed (embed/use-data-uris fonts-urls) + loading? (d/seek #(not (contains? fonts-embed %)) fonts-urls) + ;; Creates a style tag by replacing the urls with the data uri style (replace-embeds fonts-css fonts-urls fonts-embed)] (when (d/not-empty? style) - [:style style]))) + [:style {:data-loading loading?} style]))) (defn shape->fonts [shape objects] diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs index f6926234a..e6096c18b 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/thumbnail_render.cljs @@ -43,16 +43,18 @@ (.error js/console err) nil))) -(defn- remove-embed-images-changes +(defn- remove-image-loading "Remove the changes related to change a url for its embed value. This is necessary so we don't have to recalculate the thumbnail when the image loads." [value] (if (.isArray js/Array value) (->> value (remove (fn [change] - (and (= "attributes" (.-type change)) - (= "href" (.-attributeName change)) - (str/starts-with? (.-oldValue change) "http"))))) + (or + (= "data-loading" (.-attributeName change)) + (and (= "attributes" (.-type change)) + (= "href" (.-attributeName change)) + (str/starts-with? (.-oldValue change) "http")))))) [value])) (defn use-render-thumbnail @@ -64,6 +66,8 @@ disable-ref? (mf/use-var disable?) + regenerate-thumbnail (mf/use-var false) + fixed-width (mth/clamp (:width shape) 250 2000) fixed-height (/ (* (:height shape) fixed-width) (:width shape)) @@ -85,26 +89,42 @@ (st/emit! (dw/update-thumbnail page-id id thumb-data)) (reset! image-url nil)))))) + generate-thumbnail + (mf/use-callback + (fn [] + (let [node @node-ref + frame-html (dom/node->xml node) + {:keys [x y width height]} @shape-ref + + style-node (dom/query (dm/str "#frame-container-" (:id shape) " style")) + style-str (or (-> style-node dom/node->xml) "") + + svg-node + (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") + (dom/set-property! "version" "1.1") + (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) + (dom/set-property! "width" width) + (dom/set-property! "height" height) + (dom/set-property! "fill" "none") + (obj/set! "innerHTML" (dm/str style-str frame-html))) + img-src (-> svg-node dom/node->xml dom/svg->data-uri)] + (reset! image-url img-src)))) + + on-change-frame + (mf/use-callback + (fn [] + (when (and (some? @node-ref) @regenerate-thumbnail) + (let [loading-images? (some? (dom/query @node-ref "[data-loading='true']")) + loading-fonts? (some? (dom/query (dm/str "#frame-container-" (:id shape) " style[data-loading='true']")))] + (when (and (not loading-images?) (not loading-fonts?)) + (generate-thumbnail) + (reset! regenerate-thumbnail false)))))) + on-update-frame - (fn [] - (when (and (some? @node-ref) (not @disable-ref?)) - (let [node @node-ref - frame-html (dom/node->xml node) - {:keys [x y width height]} @shape-ref - - style-node (dom/query (dm/str "#frame-container-" (:id shape) " style")) - style-str (or (-> style-node dom/node->xml) "") - - svg-node - (-> (dom/make-node "http://www.w3.org/2000/svg" "svg") - (dom/set-property! "version" "1.1") - (dom/set-property! "viewBox" (dm/str x " " y " " width " " height)) - (dom/set-property! "width" width) - (dom/set-property! "height" height) - (dom/set-property! "fill" "none") - (obj/set! "innerHTML" (dm/str style-str frame-html))) - img-src (-> svg-node dom/node->xml dom/svg->data-uri)] - (reset! image-url img-src)))) + (mf/use-callback + (fn [] + (when (not @disable-ref?) + (reset! regenerate-thumbnail true)))) on-load-frame-dom (mf/use-callback @@ -120,13 +140,22 @@ (mf/use-effect (fn [] (let [subid (->> updates-str - (rx/map remove-embed-images-changes) + (rx/map remove-image-loading) (rx/filter d/not-empty?) - (rx/debounce 400) (rx/catch (fn [err] (.error js/console err))) (rx/subs on-update-frame))] #(rx/dispose! subid)))) + ;; on-change-frame will get every change in the frame + (mf/use-effect + (fn [] + (let [subid (->> updates-str + (rx/debounce 400) + (rx/observe-on :af) + (rx/catch (fn [err] (.error js/console err))) + (rx/subs on-change-frame))] + #(rx/dispose! subid)))) + (mf/use-effect (mf/deps disable?) (fn [] From d45bb0ace13fe8a148d4229da14d1bf652f2adc9 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 19 May 2022 14:15:33 +0200 Subject: [PATCH 93/98] :bug: Fix dirty text modifiers when changing pages --- frontend/src/app/main/data/workspace/texts.cljs | 9 +++++++++ .../main/ui/workspace/shapes/text/viewport_texts.cljs | 4 +++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index ec0045125..6789fe019 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -375,6 +375,15 @@ (update [_ state] (update-in state [:workspace-text-modifier id] (fnil merge {}) props)))) +(defn clean-text-modifier + [id] + (ptk/reify ::clean-text-modifier + ptk/WatchEvent + (watch [_ _ _] + (->> (rx/of #(update % :workspace-text-modifier dissoc id)) + ;; We delay a bit the change so there is no weird transition to the user + (rx/delay 50))))) + (defn remove-text-modifier [id] (ptk/reify ::remove-text-modifier diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index b8e726951..a02b70682 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -68,7 +68,9 @@ ;; Update the position-data of every text fragment (let [position-data (utp/calc-position-data node)] - (st/emit! (dwt/update-position-data id position-data)))) + (st/emit! (dwt/update-position-data id position-data))) + + (st/emit! (dwt/clean-text-modifier id))) (defn- update-text-modifier [{:keys [grow-type id]} node] From 93b582c385d23cb7d8d5dd44250266a3f852eca8 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 19 May 2022 14:16:48 +0200 Subject: [PATCH 94/98] :bug: Fix problem with small with texts --- frontend/src/app/main/ui/shapes/text/styles.cljs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/shapes/text/styles.cljs b/frontend/src/app/main/ui/shapes/text/styles.cljs index f6490e5fb..fb63acd0a 100644 --- a/frontend/src/app/main/ui/shapes/text/styles.cljs +++ b/frontend/src/app/main/ui/shapes/text/styles.cljs @@ -21,7 +21,7 @@ :width width :fontFamily "sourcesanspro" :display "flex" - :whiteSpace "pre-wrap"}] + :whiteSpace "break-spaces"}] (cond-> base (= valign "top") (obj/set! "alignItems" "flex-start") (= valign "center") (obj/set! "alignItems" "center") @@ -83,7 +83,9 @@ :lineHeight (or line-height "1.2") :color (if show-text? text-color "transparent") :caretColor (or text-color "black") - :overflowWrap "initial"} + :overflowWrap "initial" + :lineBreak "auto" + :whiteSpace "break-spaces"} fills (cond @@ -126,7 +128,4 @@ (obj/set! "fontWeight" font-weight)) (= grow-type :auto-width) - (obj/set! "whiteSpace" "pre") - - (not= grow-type :auto-width) - (obj/set! "whiteSpace" "pre-wrap"))))) + (obj/set! "whiteSpace" "pre"))))) From d8a7402046b6c3feea595f679609db5a7b9554e5 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Thu, 19 May 2022 16:26:08 +0200 Subject: [PATCH 95/98] :bug: Fix problems with text position data --- .../app/main/ui/workspace/shapes/text/viewport_texts.cljs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index a02b70682..dcc044686 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -126,15 +126,11 @@ new-modifiers (-> (get modifiers id) strip-modifier)] (or (and (not (identical? old-shape new-shape)) (not= old-shape new-shape)) - - ;; The shape has changed only if its modifier is not empty and it's different - (and (not= new-modifiers old-modifiers) - (or (not (gsh/empty-modifiers? (:modifiers old-modifiers))) - (not (gsh/empty-modifiers? (:modifiers new-modifiers)))))))) + (not= new-modifiers old-modifiers)))) changed-texts (mf/use-memo - (mf/deps text-shapes) + (mf/deps text-shapes modifiers) #(->> (keys text-shapes) (filter text-change?) (map (d/getf text-shapes)))) From 43d3b06c3023a1f00731331d26ecf25f665f4dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Thu, 19 May 2022 17:47:46 +0200 Subject: [PATCH 96/98] :bug: Fix some component shapes not synced --- .../main/data/workspace/libraries_helpers.cljs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index 700e87c0e..5172481ca 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -549,18 +549,19 @@ (:shapes shape-main)) only-inst (fn [changes child-inst] - (when-not (and omit-touched? - (contains? (:touched shape-inst) - :shapes-group)) + (if-not (and omit-touched? + (contains? (:touched shape-inst) + :shapes-group)) (remove-shape changes child-inst container - omit-touched?))) + omit-touched?) + changes)) only-main (fn [changes child-main] - (when-not (and omit-touched? - (contains? (:touched shape-inst) - :shapes-group)) + (if-not (and omit-touched? + (contains? (:touched shape-inst) + :shapes-group)) (add-shape-to-instance changes child-main (d/index-of children-main @@ -570,7 +571,8 @@ root-inst root-main omit-touched? - set-remote-synced?))) + set-remote-synced?) + changes)) both (fn [changes child-inst child-main] (generate-sync-shape-direct-recursive changes From 13492f5ac7326a7b6bc68631170e42d0399ae84c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s=20Moya?= Date: Fri, 20 May 2022 09:07:41 +0200 Subject: [PATCH 97/98] :bug: Fix orphaned component instances --- backend/src/app/srepl/main.clj | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/backend/src/app/srepl/main.clj b/backend/src/app/srepl/main.clj index be90bec85..fac42d0b0 100644 --- a/backend/src/app/srepl/main.clj +++ b/backend/src/app/srepl/main.clj @@ -67,6 +67,38 @@ (db/insert! conn :file params) (:id file)))))) +(defn repair-orphaned-components + "We have detected some cases of component instances that are not nested, but + however they have not the :component-root? attribute (so the system considers + them nested). This script fixes this adding them the attribute. + + Use it with the update-file function above." + [data] + (let [update-page + (fn [page] + (prn "================= Page:" (:name page)) + (letfn [(is-nested? [object] + (and (some? (:component-id object)) + (nil? (:component-root? object)))) + + (is-instance? [object] + (some? (:shape-ref object))) + + (get-parent [object] + (get (:objects page) (:parent-id object))) + + (update-object [object] + (if (and (is-nested? object) + (not (is-instance? (get-parent object)))) + (do + (prn "Orphan:" (:name object)) + (assoc object :component-root? true)) + object))] + + (update page :objects d/update-vals update-object)))] + + (update data :pages-index d/update-vals update-page))) + ;; (defn check-image-shapes ;; [{:keys [data] :as file} stats] ;; (println "=> analizing file:" (:name file) (:id file)) From 542fb9c7547aa1c909f578f75d4edf2a04a9f7aa Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Fri, 20 May 2022 09:22:41 +0200 Subject: [PATCH 98/98] :bug: Fix problem with nested constraints and text --- .../shapes/frame/dynamic_modifiers.cljs | 24 ++++--------------- .../workspace/shapes/text/viewport_texts.cljs | 5 +++- 2 files changed, 9 insertions(+), 20 deletions(-) diff --git a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs index 14620a762..70adcdfb6 100644 --- a/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/frame/dynamic_modifiers.cljs @@ -81,7 +81,6 @@ (let [shape-node (dom/query base-node (str "#shape-" id)) frame? (= :frame type) - text? (= :text type) group? (= :group type) mask? (and group? masked-group?)] @@ -104,10 +103,6 @@ (dom/query-all shape-defs ".svg-def") (dom/query-all shape-defs ".svg-mask-wrapper"))) - text? - [shape-node - (dom/query shape-node ".text-container")] - :else [shape-node]))) @@ -172,10 +167,12 @@ (defn update-transform! [base-node shapes transforms modifiers] - (doseq [{:keys [id] :as shape} shapes] + (doseq [{:keys [id type] :as shape} shapes] (when-let [nodes (get-nodes base-node shape)] (let [transform (get transforms id) - modifiers (get-in modifiers [id :modifiers])] + modifiers (get-in modifiers [id :modifiers]) + text? (= type :text) + transform-text? (and text? (and (nil? (:resize-vector modifiers)) (nil? (:resize-vector-2 modifiers))))] (doseq [node nodes] (cond @@ -188,17 +185,6 @@ (dom/class? node "frame-children") (set-transform-att! node "transform" (gmt/inverse transform)) - ;; We need to update the shape transform matrix when there is a resize - ;; we do it dinamicaly here - (dom/class? node "text-container") - (let [modifiers (dissoc modifiers :displacement :rotation)] - (when (not (gsh/empty-modifiers? modifiers)) - (let [mtx (-> shape - (assoc :modifiers modifiers) - (gsh/transform-shape) - (gsh/transform-matrix {:no-flip true}))] - (override-transform-att! node "transform" mtx)))) - (or (= (dom/get-tag-name node) "mask") (= (dom/get-tag-name node) "filter")) (transform-region! node modifiers) @@ -210,7 +196,7 @@ (= (dom/get-tag-name node) "pattern") (set-transform-att! node "patternTransform" transform) - (and (some? transform) (some? node)) + (and (some? transform) (some? node) (or (not text?) transform-text?)) (set-transform-att! node "transform" transform))))))) (defn remove-transform! diff --git a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs index dcc044686..28c8b5f21 100644 --- a/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs +++ b/frontend/src/app/main/ui/workspace/shapes/text/viewport_texts.cljs @@ -30,7 +30,10 @@ (defn strip-modifier [modifier] - (d/update-when modifier :modifiers dissoc :displacement :rotation)) + (if (or (some? (get-in modifier [:modifiers :resize-vector])) + (some? (get-in modifier [:modifiers :resize-vector-2]))) + modifier + (d/update-when modifier :modifiers dissoc :displacement :rotation))) (defn process-shape [modifiers {:keys [id] :as shape}] (let [modifier (-> (get modifiers id) strip-modifier)