diff --git a/frontend/src/app/main/ui/shapes/attrs.cljs b/frontend/src/app/main/ui/shapes/attrs.cljs index e2bcf1156..1db0a15bc 100644 --- a/frontend/src/app/main/ui/shapes/attrs.cljs +++ b/frontend/src/app/main/ui/shapes/attrs.cljs @@ -9,6 +9,7 @@ [app.common.colors :as clr] [app.common.data :as d] [app.common.data.macros :as dm] + [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] [app.common.svg :as csvg] [app.common.types.shape :refer [stroke-caps-line stroke-caps-marker]] @@ -27,7 +28,7 @@ (->> values (map #(+ % width)) (str/join ",")))) -(defn get-border-radius +(defn get-border-props [shape] (case (ctsr/radius-mode shape) :radius-1 @@ -58,7 +59,7 @@ (defn add-border-props! [props shape] - (obj/merge! props (get-border-radius shape))) + (obj/merge! props (get-border-props shape))) (defn add-fill! [attrs fill-data render-id index type] @@ -145,14 +146,18 @@ attrs)) -(defn add-layer-props! +(defn add-layer-styles! [props shape] (let [opacity (:opacity shape)] (if (some? opacity) (obj/set! props "opacity" opacity) props))) -(defn get-svg-props +(defn get-layer-styles + [shape] + (add-layer-styles! #js {} shape)) + +(defn- get-svg-props [shape render-id] (let [attrs (get shape :svg-attrs {}) defs (get shape :svg-defs {})] @@ -168,82 +173,92 @@ (dissoc :id) (obj/map->obj))))) -(defn add-fill-props! - [props shape render-id] - (let [svg-attrs (get-svg-props shape render-id) - svg-style (obj/get svg-attrs "style") - - shape-type (dm/get-prop shape :type) - - shape-fills (get shape :fills []) - fill-image (get shape :fill-image) - - style (-> (obj/get props "style" #js {}) - (obj/merge! svg-style) - (add-layer-props! shape))] - - (cond - (or (some? fill-image) - (= :image shape-type) - (> (count shape-fills) 1) - (some #(some? (:fill-color-gradient %)) shape-fills)) - (obj/set! style "fill" (dm/str "url(#fill-0-" render-id ")")) - - ;; imported svgs can have fill and fill-opacity attributes - (contains? svg-style "fill") - (-> style - (obj/set! "fill" (obj/get svg-style "fill")) - (obj/set! "fillOpacity" (obj/get svg-style "fillOpacity"))) - - (obj/contains? svg-attrs "fill") - (-> style - (obj/set! "fill" (obj/get svg-attrs "fill")) - (obj/set! "fillOpacity" (obj/get svg-attrs "fillOpacity"))) - - ;; If the shape comes from an imported SVG (we know because - ;; it has the :svg-attrs atribute), and it does not have an - ;; own fill, we set a default black fill. This will be - ;; inherited by child nodes, and is for emulating the - ;; behavior of standard SVG, in that a node that has no - ;; explicit fill has a default fill of black. This may be - ;; reset to normal if a Penpot frame shape appears below - ;; (see main.ui.shapes.frame/frame-container). - (and (contains? shape :svg-attrs) - (or (= :svg-raw shape-type) - (= :group shape-type)) - (empty? shape-fills)) - (let [wstyle (get shape :wrapper-styles) - fill (obj/get wstyle "fill") - fill (d/nilv fill clr/black)] - (obj/set! style "fill" fill)) - - (d/not-empty? shape-fills) - (let [fill (d/without-nils (nth shape-fills 0))] - (add-fill! style fill render-id 0 shape-type))) - - (-> props - (obj/merge! svg-attrs) - (obj/set! "style" style)))) - -(defn get-style-props - [shape render-id] - (-> #js {} - (add-fill-props! shape render-id) - (add-border-props! shape))) - -(defn get-stroke-style - [stroke-data index render-id] - (add-stroke! #js {} stroke-data render-id index)) - (defn get-fill-style [fill-data index render-id type] (add-fill! #js {} fill-data render-id index type)) -(defn extract-border-radius-attrs - [shape] - (-> (obj/create) - (add-border-props! shape))) +(defn add-fill-props! + ([props shape render-id] + (add-fill-props! props shape 0 render-id)) -(defn get-border-radius-props - [shape] - (add-border-props! #js {} shape)) + ([props shape position render-id] + (let [shape-fills (get shape :fills) + shape-shadow (get shape :shadow) + shape-blur (get shape :blur) + + svg-attrs (get-svg-props shape render-id) + svg-styles (obj/get svg-attrs "style") + + shape-type (dm/get-prop shape :type) + + style (-> (obj/get props "style") + (obj/clone) + (obj/merge! svg-styles) + (add-layer-styles! shape)) + + url-fill? (or ^boolean (some? (:fill-image shape)) + ^boolean (cfh/image-shape? shape) + ^boolean (> (count shape-fills) 1) + ^boolean (some? (some :fill-color-gradient shape-fills)) + ^boolean (some? (some :fill-image shape-fills))) + + props (if (cfh/frame-shape? shape) + props + (if (or (some? (->> shape-shadow (remove :hidden) seq)) + (and (some? shape-blur) (not ^boolean (:hidden shape-blur)))) + (obj/set! props "filter" (dm/fmt "url(#filter-%)" render-id)) + props))] + + (cond + ;; If the shape comes from an imported SVG (we know because + ;; it has the :svg-attrs atribute), and it does not have an + ;; own fill, we set a default black fill. This will be + ;; inherited by child nodes, and is for emulating the + ;; behavior of standard SVG, in that a node that has no + ;; explicit fill has a default fill of black. This may be + ;; reset to normal if a Penpot frame shape appears below + ;; (see main.ui.shapes.frame/frame-container). + (and ^boolean (contains? shape :svg-attrs) + ^boolean (or ^boolean (= :svg-raw shape-type) + ^boolean (= :group shape-type)) + ^boolean (empty? shape-fills)) + (let [wstyle (get shape :wrapper-styles) + fill (obj/get wstyle "fill") + fill (d/nilv fill clr/black)] + (obj/set! style "fill" fill)) + + ^boolean url-fill? + (do + (obj/unset! style "fill") + (obj/unset! style "fillOpacity") + (obj/set! props "fill" (dm/fmt "url(#fill-%-%)" position render-id))) + + (and ^boolean (some? svg-styles) + ^boolean (obj/contains? svg-styles "fill")) + (let [fill (obj/get svg-styles "fill") + opacity (obj/get svg-styles "fillOpacity")] + (when (some? fill) + (obj/set! style "fill" fill)) + (when (some? opacity) + (obj/set! style "fillOpacity" opacity))) + + (and ^boolean (some? svg-attrs) + ^boolean (empty? shape-fills)) + (let [fill (obj/get svg-attrs "fill") + opacity (obj/get svg-attrs "fillOpacity")] + (when (some? fill) + (obj/set! style "fill" fill)) + (when (some? opacity) + (obj/set! style "fillOpacity" opacity))) + + ^boolean (d/not-empty? shape-fills) + (let [fill (nth shape-fills 0)] + (obj/merge! style (get-fill-style fill render-id 0 shape-type))) + + (and ^boolean (cfh/path-shape? shape) + ^boolean (empty? shape-fills)) + (obj/set! style "fill" "none")) + + (-> props + (obj/merge! svg-attrs) + (obj/set! "style" style))))) diff --git a/frontend/src/app/main/ui/shapes/circle.cljs b/frontend/src/app/main/ui/shapes/circle.cljs index 367e0854d..f241a05f1 100644 --- a/frontend/src/app/main/ui/shapes/circle.cljs +++ b/frontend/src/app/main/ui/shapes/circle.cljs @@ -8,8 +8,6 @@ (:require [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] - [app.main.ui.context :as muc] - [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]] [app.util.object :as obj] [rumext.v2 :as mf])) @@ -31,10 +29,8 @@ rx (/ w 2) ry (/ h 2) - rid (mf/use-ctx muc/render-id) - props (mf/with-memo [shape] - (-> (attrs/get-style-props shape rid) + (-> #js {} (obj/merge! #js {:cx cx :cy cy :rx rx :ry ry :transform t})))] [:& shape-custom-strokes {:shape shape} diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 8bd912047..17a8a9dad 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -419,67 +419,11 @@ (defn- build-fill-element [shape child position render-id] - (let [shape-fills (get shape :fills) - shape-shadow (get shape :shadow) - shape-blur (get shape :blur) - - type (obj/get child "type") + (let [type (obj/get child "type") props (-> (obj/get child "props") (obj/clone)) - style (-> (obj/get props "style") - (obj/clone)) - - url-fill? (or ^boolean (some? (:fill-image shape)) - ^boolean (cfh/image-shape? shape) - ^boolean (> (count shape-fills) 1) - ^boolean (some? (some :fill-color-gradient shape-fills)) - ^boolean (some? (some :fill-image shape-fills))) - - props (if (cfh/frame-shape? shape) - props - (if (or (some? (->> shape-shadow (remove :hidden) seq)) - (and (some? shape-blur) (not ^boolean (:hidden shape-blur)))) - (obj/set! props "filter" (dm/fmt "url(#filter-%)" render-id)) - props)) - - svg-attrs (attrs/get-svg-props shape render-id) - svg-styles (obj/get svg-attrs "style")] - - (cond - ^boolean url-fill? - (do - (obj/unset! style "fill") - (obj/unset! style "fillOpacity") - (obj/set! props "fill" (dm/fmt "url(#fill-%-%)" position render-id))) - - (and ^boolean (some? svg-styles) - ^boolean (obj/contains? svg-styles "fill")) - (let [fill (obj/get svg-styles "fill") - opacity (obj/get svg-styles "fillOpacity")] - (when (some? fill) - (obj/set! style "fill" fill)) - (when (some? opacity) - (obj/set! style "fillOpacity" opacity))) - - (and ^boolean (some? svg-attrs) - ^boolean (empty? shape-fills)) - (let [fill (obj/get svg-attrs "fill") - opacity (obj/get svg-attrs "fillOpacity")] - (when (some? fill) - (obj/set! style "fill" fill)) - (when (some? opacity) - (obj/set! style "fillOpacity" opacity))) - - ^boolean (d/not-empty? shape-fills) - (let [fill (nth shape-fills 0)] - (obj/merge! style (attrs/get-fill-style fill render-id 0 (dm/get-prop shape :type)))) - - (and ^boolean (cfh/path-shape? shape) - ^boolean (empty? shape-fills)) - (obj/set! style "fill" "none")) - - (let [props (obj/set! props "style" style)] - (mf/html [:> type props])))) + props (attrs/add-fill-props! props shape position render-id)] + (mf/html [:> type props]))) (defn- build-stroke-element [child value position render-id] @@ -490,11 +434,11 @@ (obj/clone) (obj/set! "fill" "none") (obj/set! "fillOpacity" "none") - (obj/merge! (attrs/get-stroke-style value position render-id))) + (attrs/add-stroke! value render-id position)) - style (if (:stroke-image value) - (obj/set! style "stroke" (dm/fmt "url(#stroke-fill-%-%)" render-id position)) - style) + style (if (:stroke-image value) + (obj/set! style "stroke" (dm/fmt "url(#stroke-fill-%-%)" render-id position)) + style) props (-> (obj/clone props) (obj/unset! "fill") @@ -537,22 +481,28 @@ shape-shadow (get shape :shadow) shape-strokes (get shape :strokes) - props #js {:id stroke-id :className "strokes"} + style (-> (obj/get props "style") + (obj/clone) + (attrs/add-layer-styles! shape)) + + props #js {:id stroke-id + :className "strokes" + :style style} + props (if ^boolean (cfh/frame-shape? shape) props - (cond + (cond-> props (and (some? shape-blur) - (not ^boolean (:hidden shape-blur))) - (obj/set! props "filter" (dm/fmt "url(#filter-blur-%)" render-id)) + (not ^boolean (:hidden shape-blur))) + (obj/set! "filter" (dm/fmt "url(#filter-blur-%)" render-id)) (and (empty? shape-fills) - (some? (->> shape-shadow (remove :hidden) seq))) - (obj/set! props "filter" (dm/fmt "url(#filter-%)" render-id))))] - + (some? (->> shape-shadow (remove :hidden) seq))) + (obj/set! "filter" (dm/fmt "url(#filter-%)" render-id))))] (when (d/not-empty? shape-strokes) [:> :g props - (for [[index value] (-> (d/enumerate shape-strokes) reverse)] + (for [[index value] (reverse (d/enumerate shape-strokes))] [:& shape-custom-stroke {:shape shape :stroke value :index index diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index c7398d130..705ce3233 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -41,8 +41,9 @@ h (dm/get-prop shape :height) t (gsh/transform-str shape) - props (mf/with-memo [shape render-id] - (-> (attrs/get-style-props shape render-id) + props (mf/with-memo [shape] + (-> #js {} + (attrs/add-border-props! shape) (obj/merge! #js {:x x :y y :width w :height h :transform t}))) path? (some? (.-d props))] @@ -72,8 +73,9 @@ show-content? (get shape :show-content) - props (mf/with-memo [shape render-id] - (-> (attrs/get-style-props shape render-id) + props (mf/with-memo [shape] + (-> #js {} + (attrs/add-border-props! shape) (obj/merge! #js {:x x :y y diff --git a/frontend/src/app/main/ui/shapes/path.cljs b/frontend/src/app/main/ui/shapes/path.cljs index ad4a7fd4b..424265f27 100644 --- a/frontend/src/app/main/ui/shapes/path.cljs +++ b/frontend/src/app/main/ui/shapes/path.cljs @@ -7,8 +7,6 @@ (ns app.main.ui.shapes.path (:require [app.common.logging :as log] - [app.main.ui.context :as muc] - [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]] [app.util.object :as obj] [app.util.path.format :as upf] @@ -24,13 +22,12 @@ (upf/format-path content) (catch :default e (log/error :hint "unexpected error on formatting path" - :shape-name (:name shape) - :shape-id (:id shape) - :cause e) - ""))) + :shape-name (:name shape) + :shape-id (:id shape) + :cause e) + ""))) - render-id (mf/use-ctx muc/render-id) - props (-> (attrs/get-style-props shape render-id) + props (-> #js {} (obj/set! "d" pdata))] [:& shape-custom-strokes {:shape shape} diff --git a/frontend/src/app/main/ui/shapes/rect.cljs b/frontend/src/app/main/ui/shapes/rect.cljs index f2bf5c03d..64b2c6cc5 100644 --- a/frontend/src/app/main/ui/shapes/rect.cljs +++ b/frontend/src/app/main/ui/shapes/rect.cljs @@ -8,7 +8,6 @@ (:require [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] - [app.main.ui.context :as muc] [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.custom-stroke :refer [shape-custom-strokes]] [app.util.object :as obj] @@ -25,10 +24,10 @@ h (dm/get-prop shape :height) t (gsh/transform-str shape) - rid (mf/use-ctx muc/render-id) - props (mf/with-memo [shape rid] - (-> (attrs/get-style-props shape rid) + props (mf/with-memo [shape] + (-> #js {} + (attrs/add-border-props! shape) (obj/merge! #js {:x x :y y :transform t :width w :height h}))) path? (some? (.-d props))] diff --git a/frontend/src/app/main/ui/shapes/svg_raw.cljs b/frontend/src/app/main/ui/shapes/svg_raw.cljs index 9758eb273..4a5642884 100644 --- a/frontend/src/app/main/ui/shapes/svg_raw.cljs +++ b/frontend/src/app/main/ui/shapes/svg_raw.cljs @@ -40,7 +40,8 @@ render-id (mf/use-ctx muc/render-id) props (mf/with-memo [shape render-id] - (-> (usa/get-style-props shape render-id) + (-> #js {} + (usa/add-fill-props! shape render-id) (obj/unset! "transform") (obj/set! "x" x) (obj/set! "y" y) @@ -78,10 +79,11 @@ props (mf/with-memo [shape render-id] (let [element-id (dm/get-in shape [:svg-attrs :id]) - props (usa/get-style-props shape render-id)] + props (-> #js {} + (usa/add-fill-props! shape render-id))] (when (and (some? element-id) - (contains? ids-mapping element-id)) + (contains? ids-mapping element-id)) (obj/set! props "id" (get ids-mapping element-id))) props))] diff --git a/frontend/src/app/main/ui/shapes/text/fo_text.cljs b/frontend/src/app/main/ui/shapes/text/fo_text.cljs index 8644be21b..f1241238d 100644 --- a/frontend/src/app/main/ui/shapes/text/fo_text.cljs +++ b/frontend/src/app/main/ui/shapes/text/fo_text.cljs @@ -191,7 +191,7 @@ :transform transform :width (if (#{:auto-width} grow-type) 100000 width) :height (if (#{:auto-height :auto-width} grow-type) 100000 height) - :style (attrs/add-layer-props! #js {} shape) + :style (attrs/get-layer-styles shape) :ref ref} ;; We use a class here because react has a bug that won't use the appropriate selector for ;; `background-clip` diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index a87178749..43efeda91 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -51,7 +51,7 @@ (d/nilv (ex/ignoring (upf/format-path content)) ""))) border-attrs - (attrs/get-border-radius shape) + (attrs/get-border-props shape) outline-type (case type