diff --git a/CHANGES.md b/CHANGES.md index ce1760a21..50063e39d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -42,6 +42,7 @@ ### :bug: Bugs fixed +- Add shadow to artboard make it lose the fill [Taiga #3139](https://tree.taiga.io/project/penpot/issue/3139) - Avoid numeric inputs to change its value without focusing them [Taiga #3140](https://tree.taiga.io/project/penpot/issue/3140) - Fix comments modal when changing pages [Taiga #2597](https://tree.taiga.io/project/penpot/issue/2508) - Copy paste inside a text layer leaves pasted text transparent [Taiga #3096](https://tree.taiga.io/project/penpot/issue/3096) diff --git a/frontend/src/app/main/ui/render.cljs b/frontend/src/app/main/ui/render.cljs index 5572587d1..e731d4555 100644 --- a/frontend/src/app/main/ui/render.cljs +++ b/frontend/src/app/main/ui/render.cljs @@ -33,10 +33,10 @@ (let [xf-get-bounds (comp (map #(get objects %)) (map #(calc-bounds % objects))) padding (filters/calculate-padding object) obj-bounds (-> (filters/get-filters-bounds object) - (update :x - padding) - (update :y - padding) - (update :width + (* 2 padding)) - (update :height + (* 2 padding)))] + (update :x - (:horizontal padding)) + (update :y - (:vertical padding)) + (update :width + (* 2 (:horizontal padding))) + (update :height + (* 2 (:vertical padding))))] (cond (and (= :group (:type object)) diff --git a/frontend/src/app/main/ui/shapes/custom_stroke.cljs b/frontend/src/app/main/ui/shapes/custom_stroke.cljs index 2c951ecdf..a5b24efa1 100644 --- a/frontend/src/app/main/ui/shapes/custom_stroke.cljs +++ b/frontend/src/app/main/ui/shapes/custom_stroke.cljs @@ -9,6 +9,7 @@ [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.shapes :as gsh] + [app.common.pages.helpers :as cph] [app.main.ui.context :as muc] [app.main.ui.shapes.attrs :as attrs] [app.main.ui.shapes.gradients :as grad] @@ -327,8 +328,12 @@ (obj/clone)) props (cond-> props - (d/not-empty? (:shadow shape)) - (obj/set! "filter" (dm/fmt "url(#filter_%)" render-id))) + (or + ;; There are any shadows + (and (d/not-empty? (:shadow shape)) (not (cph/frame-shape? shape))) + ;; There are no strokes and a blur + (and (some? (:blur shape)) (not (cph/frame-shape? shape)) (empty? (:strokes shape)))) + (obj/set! "filter" (dm/fmt "url(#filter_%)" render-id))) svg-defs (:svg-defs shape {}) svg-attrs (:svg-attrs shape {}) @@ -391,7 +396,19 @@ (obj/set! "fillOpacity" "none"))) (add-style (obj/get (attrs/extract-stroke-attrs value position render-id) "style"))))) -(mf/defc shape-custom-strokes + +(mf/defc shape-fills + {::mf/wrap-props false} + [props] + (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)] + + [:g {:id (dm/fmt "fills-%" (:id shape))} + [:> elem-name (build-fill-props shape child render-id)]])) + +(mf/defc shape-strokes {::mf/wrap-props false} [props] (let [child (obj/get props "children") @@ -401,13 +418,9 @@ stroke-props (-> (obj/new) (obj/set! "id" (dm/fmt "strokes-%" (:id shape))) (cond-> - (some? (:blur shape)) + (and (some? (:blur shape)) (not (cph/frame-shape? shape))) (obj/set! "filter" (dm/fmt "url(#filter_blur_%)" render-id))))] - [:* - [:g {:id (dm/fmt "fills-%" (:id shape))} - [:> elem-name (build-fill-props shape child render-id)]] - (when (d/not-empty? (:strokes shape)) [:> :g stroke-props @@ -416,3 +429,16 @@ shape (assoc value :points (:points shape))] [:& shape-custom-stroke {:shape shape :index index} [:> elem-name props]]))])])) + +(mf/defc shape-custom-strokes + {::mf/wrap-props false} + [props] + (let [child (obj/get props "children") + shape (obj/get props "shape")] + + [:* + [:& shape-fills {:shape shape} + child] + + [:& shape-strokes {:shape shape} + child]])) diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index a1370b887..378a22397 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -170,7 +170,6 @@ ([shape filters blur-value] (let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag]))) - frame? (= :frame (:type shape)) {:keys [x y width height]} (:selrect shape)] (if svg-root? ;; When is a raw-svg but not the root we use the whole svg as bound for the filter. Is the maximum @@ -183,6 +182,7 @@ (map (partial filter-bounds shape))) ;; We add the selrect so the minimum size will be the selrect filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect)) + x1 (apply min (map :x1 filter-bounds)) y1 (apply min (map :y1 filter-bounds)) x2 (apply max (map :x2 filter-bounds)) @@ -195,18 +195,30 @@ ;; We should move the frame filter coordinates because they should be ;; relative with the frame. By default they come as absolute - {:x (if frame? (- x1 x) x1) - :y (if frame? (- y1 y) y1) + {:x x1 + :y y1 :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))) - margin (apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape)))] - (+ stroke-width margin))) + :center (/ (:stroke-width % 0) 2) + :outer (:stroke-width % 0) + 0) (:strokes shape))) + + margin (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-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)})) (defn change-filter-in "Adds the previous filter as `filter-in` parameter" @@ -220,10 +232,10 @@ bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0)) padding (calculate-padding shape) selrect (:selrect shape) - filter-x (/ (- (:x bounds) (:x selrect) padding) (:width selrect)) - filter-y (/ (- (:y bounds) (:y selrect) padding) (:height selrect)) - filter-width (/ (+ (:width bounds) (* 2 padding)) (:width selrect)) - filter-height (/ (+ (:height bounds) (* 2 padding)) (:height selrect))] + filter-x (/ (- (:x bounds) (:x selrect) (:horizontal padding)) (:width selrect)) + filter-y (/ (- (:y bounds) (:y selrect) (:vertical padding)) (:height selrect)) + filter-width (/ (+ (:width bounds) (* 2 (:horizontal padding))) (:width selrect)) + filter-height (/ (+ (:height bounds) (* 2 (:vertical padding))) (:height selrect))] (when (> (count filters) 2) [:filter {:id filter-id :x filter-x diff --git a/frontend/src/app/main/ui/shapes/frame.cljs b/frontend/src/app/main/ui/shapes/frame.cljs index 42be91bc9..1e2d85df5 100644 --- a/frontend/src/app/main/ui/shapes/frame.cljs +++ b/frontend/src/app/main/ui/shapes/frame.cljs @@ -7,9 +7,10 @@ (ns app.main.ui.shapes.frame (: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.main.ui.shapes.filters :as filters] + [app.main.ui.shapes.custom-stroke :refer [shape-fills shape-strokes]] [app.util.object :as obj] [debug :refer [debug?]] [rumext.alpha :as mf])) @@ -27,13 +28,12 @@ [{:keys [shape render-id]}] (when (= :frame (:type shape)) (let [{:keys [x y width height]} shape - padding (filters/calculate-padding shape) props (-> (attrs/extract-style-attrs shape) (obj/merge! - #js {:x (- x padding) - :y (- y padding) - :width (+ width (* 2 padding)) - :height (+ height (* 2 padding))})) + #js {:x x + :y y + :width width + :height height})) path? (some? (.-d props))] [:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"} (if path? @@ -63,22 +63,32 @@ (let [childs (unchecked-get props "childs") shape (unchecked-get props "shape") {: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 transform :width width :height height :className "frame-background"})) - path? (some? (.-d props))] - - [:* - [:& shape-custom-strokes {:shape shape} - (if path? - [:> :path props] - [:> :rect props])] - (for [item childs] - [:& shape-wrapper {:shape item - :key (dm/str (:id item))}])]))) + path? (some? (.-d props)) + render-id (mf/use-ctx muc/render-ctx)] + + [:* + [:g {:clip-path (frame-clip-url shape render-id)} + [:* + [:& shape-fills {:shape shape} + (if path? + [:> :path props] + [:> :rect props])] + + (for [item childs] + [:& shape-wrapper {:shape item + :key (dm/str (:id item))}]) + [:& shape-strokes {:shape shape} + (if path? + [:> :path props] + [:> :rect props])]]]]))) diff --git a/frontend/src/app/main/ui/shapes/shape.cljs b/frontend/src/app/main/ui/shapes/shape.cljs index a4948df44..7a24ba008 100644 --- a/frontend/src/app/main/ui/shapes/shape.cljs +++ b/frontend/src/app/main/ui/shapes/shape.cljs @@ -50,14 +50,11 @@ wrapper-props (cond-> wrapper-props - (some #(= (:type shape) %) [:group :svg-raw]) + (some #(= (:type shape) %) [:group :svg-raw :frame]) (obj/set! "filter" (filters/filter-str filter-id shape))) wrapper-props (cond-> wrapper-props - (= :frame type) - (obj/set! "clipPath" (frame/frame-clip-url shape render-id)) - (= :group type) (attrs/add-style-attrs shape render-id))] diff --git a/frontend/src/app/main/ui/viewer.cljs b/frontend/src/app/main/ui/viewer.cljs index a7c3e860d..e8204b59e 100644 --- a/frontend/src/app/main/ui/viewer.cljs +++ b/frontend/src/app/main/ui/viewer.cljs @@ -32,12 +32,17 @@ (defn- calculate-size [frame zoom] - (let [{:keys [_ _ width height]} (filters/get-filters-bounds frame)] + (let [{:keys [_ _ width height]} (filters/get-filters-bounds frame) + padding (filters/calculate-padding frame) + x (- (:horizontal padding)) + y (- (:vertical padding)) + width (+ width (* 2 (:horizontal padding))) + height (+ height (* 2 (:vertical padding)))] {:base-width width :base-height height :width (* width zoom) :height (* height zoom) - :vbox (str "0 0 " width " " height)})) + :vbox (str x " " y " " width " " height)})) (defn- calculate-wrapper [size1 size2 zoom] diff --git a/frontend/src/app/main/ui/viewer/handoff/attributes.cljs b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs index 52518b738..ae9e5fe93 100644 --- a/frontend/src/app/main/ui/viewer/handoff/attributes.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/attributes.cljs @@ -21,7 +21,7 @@ (def type->options {:multiple [:fill :stroke :image :text :shadow :blur] - :frame [:layout :fill :stroke] + :frame [:layout :fill :stroke :shadow :blur] :group [:layout :svg] :rect [:layout :fill :stroke :shadow :blur :svg] :circle [:layout :fill :stroke :shadow :blur :svg] diff --git a/frontend/src/app/main/ui/viewer/handoff/render.cljs b/frontend/src/app/main/ui/viewer/handoff/render.cljs index 5fb32104b..c7e4bfe02 100644 --- a/frontend/src/app/main/ui/viewer/handoff/render.cljs +++ b/frontend/src/app/main/ui/viewer/handoff/render.cljs @@ -13,6 +13,7 @@ [app.main.store :as st] [app.main.ui.shapes.bool :as bool] [app.main.ui.shapes.circle :as circle] + [app.main.ui.shapes.filters :as filters] [app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.group :as group] [app.main.ui.shapes.image :as image] @@ -190,9 +191,18 @@ frame (get objects (:id frame)) zoom (:zoom local 1) - width (* (:width frame) zoom) - height (* (:height frame) zoom) - vbox (str "0 0 " (:width frame 0) " " (:height frame 0)) + + {:keys [_ _ width height]} (filters/get-filters-bounds frame) + padding (filters/calculate-padding frame) + x (- (:horizontal padding)) + y (- (:vertical padding)) + width (+ width (* 2 (:horizontal padding))) + height (+ height (* 2 (:vertical padding))) + + vbox (str x " " y " " width " " height) + + width (* width zoom) + height (* height zoom) render (mf/use-memo (mf/deps objects) diff --git a/frontend/src/app/main/ui/viewer/shapes.cljs b/frontend/src/app/main/ui/viewer/shapes.cljs index ab39c835e..c7390b6a7 100644 --- a/frontend/src/app/main/ui/viewer/shapes.cljs +++ b/frontend/src/app/main/ui/viewer/shapes.cljs @@ -403,9 +403,9 @@ modifier-ids (into [frame-id] (cph/get-children-ids objects frame-id)) objects (reduce update-fn objects modifier-ids) frame (assoc-in frame [:modifiers :displacement] modifier) - width (* (:width frame) zoom) height (* (:height frame) zoom) + vbox (str "0 0 " (:width frame 0) " " (:height frame 0)) wrapper (mf/use-memo