diff --git a/CHANGES.md b/CHANGES.md index 9f87f4c9a..66073f114 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -125,6 +125,10 @@ - [VIEWER] Cannot scroll down in code mode [Taiga #4655](https://tree.taiga.io/project/penpot/issue/4655) - Strange cursor behavior after clicking viewport with text tool [Taiga #4363](https://tree.taiga.io/project/penpot/issue/4363) - Selected color affects all of them [Taiga #5285](https://tree.taiga.io/project/penpot/issue/5285) +- Fix problem with shadow negative spread [Github #3421](https://github.com/penpot/penpot/issues/3421) +- Fix problem with linked colors to strokes [Github #3522](https://github.com/penpot/penpot/issues/3522) +- Fix problem with hand tool stuck [Github #3318](https://github.com/penpot/penpot/issues/3318) +- Fix problem with fix scrolling on nested elements [Github #3508](https://github.com/penpot/penpot/issues/3508) ## 1.19.5 diff --git a/backend/src/app/binfile/common.clj b/backend/src/app/binfile/common.clj index d5a4241e2..ace98c80e 100644 --- a/backend/src/app/binfile/common.clj +++ b/backend/src/app/binfile/common.clj @@ -277,7 +277,7 @@ (update :fill-color-ref-file lookup-index) ;; This covers the old shapes and the new :strokes - (uuid? (:storage-color-ref-file form)) + (uuid? (:stroke-color-ref-file form)) (update :stroke-color-ref-file lookup-index) ;; This covers all text shapes that have typography referenced diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index 5b31328e6..6203d5db1 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -762,6 +762,13 @@ (recur frame-id frame-parents (rest selected)))))) +(defn fixed-scroll? + [shape] + ^boolean + (and (:fixed-scroll shape) + (= (:parent-id shape) (:frame-id shape)) + (not= (:frame-id shape) uuid/zero))) + (defn fixed? [objects shape-id] (let [ids-to-check @@ -772,4 +779,4 @@ (take-while #(and (not= % uuid/zero) (not (root-frame? objects %))))))] (boolean (->> ids-to-check - (d/seek (fn [id] (dm/get-in objects [id :fixed-scroll]))))))) + (d/seek (fn [id] () (fixed-scroll? (get objects id)))))))) diff --git a/frontend/src/app/main/data/workspace/modifiers.cljs b/frontend/src/app/main/data/workspace/modifiers.cljs index d27b980f6..c76c91742 100644 --- a/frontend/src/app/main/data/workspace/modifiers.cljs +++ b/frontend/src/app/main/data/workspace/modifiers.cljs @@ -18,6 +18,7 @@ [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.modifiers :as ctm] + [app.common.types.shape-tree :as ctst] [app.common.types.shape.attrs :refer [editable-attrs]] [app.common.types.shape.layout :as ctl] [app.common.uuid :as uuid] @@ -193,13 +194,16 @@ (update :shapes #(d/removev ids %)) (ctl/assign-cells objects)) - ids (->> ids (remove #(ctl/position-absolute? objects %))) + ids (->> ids + (remove #(ctl/position-absolute? objects %)) + (ctst/sort-z-index objects) + reverse) + frame (-> frame (update :shapes d/concat-vec ids) (cond-> (some? cell) (ctl/push-into-cell ids row column)) (ctl/assign-cells objects))] - (-> modifiers (ctm/change-property :layout-grid-rows (:layout-grid-rows frame)) (ctm/change-property :layout-grid-columns (:layout-grid-columns frame)) diff --git a/frontend/src/app/main/data/workspace/texts.cljs b/frontend/src/app/main/data/workspace/texts.cljs index c3e98739b..492cc6a83 100644 --- a/frontend/src/app/main/data/workspace/texts.cljs +++ b/frontend/src/app/main/data/workspace/texts.cljs @@ -601,12 +601,16 @@ attrs (-> typography (assoc :typography-ref-file file-id) (assoc :typography-ref-id (:id typography)) - (dissoc :id :name))] + (dissoc :id :name)) + undo-id (js/Symbol)] - (->> (rx/from (seq selected)) - (rx/map (fn [id] - (let [editor (get editor-state id)] - (update-text-attrs {:id id :editor editor :attrs attrs}))))))))) + (rx/concat + (rx/of (dwu/start-undo-transaction undo-id)) + (->> (rx/from (seq selected)) + (rx/map (fn [id] + (let [editor (get editor-state id)] + (update-text-attrs {:id id :editor editor :attrs attrs}))))) + (rx/of (dwu/commit-undo-transaction undo-id))))))) (defn generate-typography-name [{:keys [font-id font-variant-id] :as typography}] diff --git a/frontend/src/app/main/streams.cljs b/frontend/src/app/main/streams.cljs index 4fc3d970d..d6ebf074b 100644 --- a/frontend/src/app/main/streams.cljs +++ b/frontend/src/app/main/streams.cljs @@ -151,6 +151,11 @@ (rx/filter kbd/space?) (rx/filter (complement kbd/editing-event?)) (rx/map kbd/key-down-event?) + ;; Fix a situation caused by using `ctrl+alt` kind of + ;; shortcuts, that makes keyboard-alt stream + ;; registering the key pressed but on blurring the + ;; window (unfocus) the key down is never arrived. + (rx/merge window-blur) (rx/pipe (rxo/distinct-contiguous)))] (rx/sub! ob sub) sub)) diff --git a/frontend/src/app/main/ui.cljs b/frontend/src/app/main/ui.cljs index 69c83db69..d00e466c7 100644 --- a/frontend/src/app/main/ui.cljs +++ b/frontend/src/app/main/ui.cljs @@ -107,8 +107,7 @@ (not= "0.0" (:main cf/version))) [:& release-notes-modal {:version (:main cf/version)}])) - (when profile - [:& dashboard-page {:route route :profile profile}])] + [:& dashboard-page {:route route :profile profile}]] :viewer (let [{:keys [query-params path-params]} route diff --git a/frontend/src/app/main/ui/components/forms.cljs b/frontend/src/app/main/ui/components/forms.cljs index b7ad29e5f..7ae43e1e9 100644 --- a/frontend/src/app/main/ui/components/forms.cljs +++ b/frontend/src/app/main/ui/components/forms.cljs @@ -372,7 +372,10 @@ in-klass (str class " " (stl/css-case :inside-input true - :no-padding (pos? (count @items)))) + :no-padding (pos? (count @items)) + :invalid (and (some? valid-item-fn) + touched? + (not (valid-item-fn @value))))) on-focus (mf/use-fn #(reset! focus? true)) @@ -394,27 +397,38 @@ (mf/use-fn (mf/deps @value) (fn [event] - (cond - (or (kbd/enter? event) - (kbd/comma? event)) - (do - (dom/prevent-default event) - (dom/stop-propagation event) - (let [val (cond-> @value trim str/trim)] + (let [val (cond-> @value trim str/trim)] + (cond + (or (kbd/enter? event) (kbd/comma? event) (kbd/space? event)) + (do + (dom/prevent-default event) + (dom/stop-propagation event) + + ;; Once enter/comma is pressed we mark it as touched + (swap! form assoc-in [:touched input-name] true) + + ;; Empty values means "submit" the form (whent some items have been added (when (and (kbd/enter? event) (str/empty? @value) (not-empty @items)) (on-submit form)) - (when (not (str/empty? @value)) - (reset! value "") - (swap! items conj-dedup {:text val - :valid (valid-item-fn val) - :caution (caution-item-fn val)})))) - (and (kbd/backspace? event) - (str/empty? @value)) - (do - (dom/prevent-default event) - (dom/stop-propagation event) - (swap! items (fn [items] (if (c/empty? items) items (pop items)))))))) + ;; If we have a string in the input we add it only if valid + (when (and (valid-item-fn val) (not (str/empty? @value))) + (reset! value "") + + ;; Once added the form is back as "untouched" + (swap! form assoc-in [:touched input-name] false) + + ;; This split will allow users to copy comma/space separated values of emails + (doseq [val (str/split val #",|\s+")] + (swap! items conj-dedup {:text (str/trim val) + :valid (valid-item-fn val) + :caution (caution-item-fn val)})))) + + (and (kbd/backspace? event) (str/empty? @value)) + (do + (dom/prevent-default event) + (dom/stop-propagation event) + (swap! items (fn [items] (if (c/empty? items) items (pop items))))))))) on-blur (mf/use-fn diff --git a/frontend/src/app/main/ui/components/forms.scss b/frontend/src/app/main/ui/components/forms.scss index d77fce23b..19027a065 100644 --- a/frontend/src/app/main/ui/components/forms.scss +++ b/frontend/src/app/main/ui/components/forms.scss @@ -293,6 +293,13 @@ outline: none; border: $s-1 solid var(--input-border-color-focus); } + &.invalid { + border: $s-1 solid var(--input-border-color-error); + &:hover, + &:focus { + border: $s-1 solid var(--input-border-color-error); + } + } } label { display: none; diff --git a/frontend/src/app/main/ui/dashboard/team.cljs b/frontend/src/app/main/ui/dashboard/team.cljs index a2f80d058..4307d66cc 100644 --- a/frontend/src/app/main/ui/dashboard/team.cljs +++ b/frontend/src/app/main/ui/dashboard/team.cljs @@ -787,24 +787,25 @@ on-error (mf/use-fn - (fn [form {:keys [type code hint] :as error}] - (if (and (= type :validation) - (= code :webhook-validation)) - (let [message (cond - (= hint "unknown") - (tr "errors.webhooks.unexpected") - (= hint "invalid-uri") - (tr "errors.webhooks.invalid-uri") - (= hint "ssl-validation-error") - (tr "errors.webhooks.ssl-validation") - (= hint "timeout") - (tr "errors.webhooks.timeout") - (= hint "connection-error") - (tr "errors.webhooks.connection") - (str/starts-with? hint "unexpected-status") - (tr "errors.webhooks.unexpected-status" (extract-status hint)))] - (swap! form assoc-in [:errors :uri] {:message message})) - (rx/throw error)))) + (fn [form error] + (let [{:keys [type code hint]} (ex-data error)] + (if (and (= type :validation) + (= code :webhook-validation)) + (let [message (cond + (= hint "unknown") + (tr "errors.webhooks.unexpected") + (= hint "invalid-uri") + (tr "errors.webhooks.invalid-uri") + (= hint "ssl-validation-error") + (tr "errors.webhooks.ssl-validation") + (= hint "timeout") + (tr "errors.webhooks.timeout") + (= hint "connection-error") + (tr "errors.webhooks.connection") + (str/starts-with? hint "unexpected-status") + (tr "errors.webhooks.unexpected-status" (extract-status hint)))] + (swap! form assoc-in [:errors :uri] {:message message})) + (rx/throw error))))) on-create-submit (mf/use-fn diff --git a/frontend/src/app/main/ui/shapes/filters.cljs b/frontend/src/app/main/ui/shapes/filters.cljs index 8ac7a15af..f7ff505db 100644 --- a/frontend/src/app/main/ui/shapes/filters.cljs +++ b/frontend/src/app/main/ui/shapes/filters.cljs @@ -45,6 +45,12 @@ :in "SourceAlpha" :result filter-id}]) + (when (< spread 0) + [:feMorphology {:radius (- spread) + :operator "erode" + :in "SourceAlpha" + :result filter-id}]) + [:feOffset {:dx offset-x :dy offset-y}] [:feGaussianBlur {:stdDeviation (/ blur 2)}] [:& color-matrix {:color color}] diff --git a/frontend/src/app/main/ui/viewer/inspect/attributes/stroke.cljs b/frontend/src/app/main/ui/viewer/inspect/attributes/stroke.cljs index e8e1ebcd3..5d5567160 100644 --- a/frontend/src/app/main/ui/viewer/inspect/attributes/stroke.cljs +++ b/frontend/src/app/main/ui/viewer/inspect/attributes/stroke.cljs @@ -7,18 +7,14 @@ (ns app.main.ui.viewer.inspect.attributes.stroke (:require-macros [app.main.style :as stl]) (:require - [app.common.data :as d] - [app.common.data.macros :as dm] - [app.main.ui.components.copy-button :refer [copy-button]] [app.main.ui.components.title-bar :refer [inspect-title-bar]] - [app.main.ui.formats :as fmt] [app.main.ui.viewer.inspect.attributes.common :refer [color-row]] - [app.util.code-gen.style-css-formats :as cssf] - [app.util.code-gen.style-css-values :as cssv] - [app.util.color :as uc] + [app.util.code-gen.style-css :as css] [app.util.i18n :refer [tr]] [rumext.v2 :as mf])) +(def properties [:border]) + (defn stroke->color [shape] {:color (:stroke-color shape) :opacity (:stroke-opacity shape) @@ -31,30 +27,21 @@ (seq (:strokes shape))) (mf/defc stroke-block - [{:keys [stroke]}] - (let [color-format (mf/use-state :hex) - color (stroke->color stroke)] - [:div {:class (stl/css :attributes-stroke-block)} - (let [{:keys [stroke-style stroke-alignment]} stroke - stroke-style (if (= stroke-style :svg) :solid stroke-style) - stroke-alignment (or stroke-alignment :center) - stroke-def (dm/str (fmt/format-pixels (:stroke-width stroke)) " " - (tr (dm/str "inspect.attributes.stroke.style." (or (d/name stroke-style) "none"))) " " - (tr (dm/str "inspect.attributes.stroke.alignment." (d/name stroke-alignment))))] - - [:* - [:& color-row {:color color - :format @color-format - :copy-data (uc/color->background color) - :on-change-format #(reset! color-format %)}] - - [:div {:class (stl/css :stroke-row)} - [:div {:class (stl/css :global/attr-label)} - "Border"] - [:div {:class (stl/css :global/attr-value)} - - [:& copy-button {:data (cssf/format-value :border (cssv/get-stroke-data stroke))} - [:div {:class (stl/css :button-children)} stroke-def]]]]])])) + {::mf/wrap-props false} + [{:keys [objects shape]}] + (let [format* (mf/use-state :hex) + format (deref format*) + color (stroke->color shape) + on-change + (mf/use-fn + (fn [format] + (reset! format* format)))] + [:div {:class (stl/css :attributes-fill-block)} + [:& color-row + {:color color + :format format + :on-change-format on-change + :copy-data (css/get-shape-properties-css objects {:strokes [shape]} properties)}]])) (mf/defc stroke-panel [{:keys [shapes]}] @@ -69,4 +56,4 @@ (for [shape shapes] (for [value (:strokes shape)] [:& stroke-block {:key (str "stroke-color-" (:id shape) value) - :stroke value}]))]]))) + :shape value}]))]]))) diff --git a/frontend/src/app/main/ui/viewer/interactions.cljs b/frontend/src/app/main/ui/viewer/interactions.cljs index dc4f30ef1..03910ea5d 100644 --- a/frontend/src/app/main/ui/viewer/interactions.cljs +++ b/frontend/src/app/main/ui/viewer/interactions.cljs @@ -41,7 +41,7 @@ (defn get-fixed-ids [objects] - (let [fixed-ids (filter :fixed-scroll (vals objects)) + (let [fixed-ids (filter cfh/fixed-scroll? (vals objects)) ;; we have to consider the children if the fixed element is a group fixed-children-ids diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs index d9e543d19..d40d0ef42 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/interactions.cljs @@ -86,7 +86,7 @@ accept-edit (fn [] (let [name-input (mf/ref-val name-ref) - name (dom/get-value name-input)] + name (str/trim (dom/get-value name-input))] (reset! editing? false) (st/emit! (dwi/end-rename-flow) (when-not (str/empty? name)