diff --git a/CHANGES.md b/CHANGES.md index 33a99e3811..569ddf65d5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -30,6 +30,7 @@ - Fix issue where Alt + arrow keys shortcut interferes with letter-spacing when moving text layers [Taiga #11552](https://tree.taiga.io/project/penpot/issue/11771) - Fix consistency issues on how font variants are visualized [Taiga #11499](https://tree.taiga.io/project/penpot/us/11499) - Fix parsing rx and ry SVG values for rect radius [Taiga #11861](https://tree.taiga.io/project/penpot/issue/11861) +- Misleading affordance in saved versions [Taiga #11887](https://tree.taiga.io/project/penpot/issue/11887) ## 2.9.0 diff --git a/common/src/app/common/files/helpers.cljc b/common/src/app/common/files/helpers.cljc index 4a12b22f64..afb0c4f4af 100644 --- a/common/src/app/common/files/helpers.cljc +++ b/common/src/app/common/files/helpers.cljc @@ -821,6 +821,13 @@ (let [path-split (split-path path)] (merge-path-item (first path-split) name))) +(defn inside-path? [child parent] + (let [child-path (split-path child) + parent-path (split-path parent)] + (and (<= (count parent-path) (count child-path)) + (= parent-path (take (count parent-path) child-path))))) + + (defn split-by-last-period "Splits a string into two parts: diff --git a/common/src/app/common/flags.cljc b/common/src/app/common/flags.cljc index 49a71cd04e..c395be0e41 100644 --- a/common/src/app/common/flags.cljc +++ b/common/src/app/common/flags.cljc @@ -131,7 +131,8 @@ :hide-release-modal :subscriptions :subscriptions-old - :frontend-binary-fills}) + :frontend-binary-fills + :inspect-styles}) (def all-flags (set/union email login varia)) diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index 5ade4c3cca..811f3b4006 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -569,57 +569,56 @@ combine (fn [current-page] (let [objects (dsh/lookup-page-objects state current-page) - selected (or selected - (->> (dsh/lookup-selected state) - (cfh/clean-loops objects) - (remove (fn [id] - (let [shape (get objects id)] - (or (not (ctc/main-instance? shape)) - (ctc/is-variant? shape))))))) - shapes (mapv #(get objects %) selected) - rect (bounding-rect shapes) - prefix (->> shapes - (mapv #(cfh/split-path (:name %))) - (common-prefix)) - ;; When the common parent is root, add a wrapper - add-wrapper? (= prefix []) - first-shape (first shapes) - delta (gpt/point (- (:x rect) (:x first-shape) 30) - (- (:y rect) (:y first-shape) 30)) - common-parent (->> selected - (mapv #(-> (cfh/get-parent-ids objects %) reverse)) - common-prefix - last) - index (-> (get objects common-parent) - :shapes - count - inc) - variant-id (uuid/next) - undo-id (js/Symbol)] + selected (->> (or selected (dsh/lookup-selected state)) + (cfh/clean-loops objects) + (remove (fn [id] + (let [shape (get objects id)] + (or (not (ctc/main-instance? shape)) + (ctc/is-variant? shape))))))] + (when (> (count selected) 1) + (let [shapes (mapv #(get objects %) selected) + rect (bounding-rect shapes) + prefix (->> shapes + (mapv #(cfh/split-path (:name %))) + (common-prefix)) + ;; When the common parent is root, add a wrapper + add-wrapper? (empty? prefix) + first-shape (first shapes) + delta (gpt/point (- (:x rect) (:x first-shape) 30) + (- (:y rect) (:y first-shape) 30)) + common-parent (->> selected + (mapv #(-> (cfh/get-parent-ids objects %) reverse)) + common-prefix + last) + index (-> (get objects common-parent) + :shapes + count + inc) + variant-id (uuid/next) + undo-id (js/Symbol)] - (rx/concat - (if (and page-id (not= current-page page-id)) - (rx/of (dcm/go-to-workspace :page-id page-id)) - (rx/empty)) + (rx/concat + (if (and page-id (not= current-page page-id)) + (rx/of (dcm/go-to-workspace :page-id page-id)) + (rx/empty)) - (rx/of (dwu/start-undo-transaction undo-id) - (transform-in-variant (first selected) variant-id delta prefix add-wrapper? false false) - (dwsh/relocate-shapes (into #{} (-> selected rest reverse)) variant-id 0) - (dwsh/update-shapes selected #(-> % - (assoc :constraints-h :left) - (assoc :constraints-v :top) - (assoc :fixed-scroll false))) - (dwsh/relocate-shapes #{variant-id} common-parent index) - (dwt/update-dimensions [variant-id] :width (+ (:width rect) 60)) - (dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))) + (rx/of (dwu/start-undo-transaction undo-id) + (transform-in-variant (first selected) variant-id delta prefix add-wrapper? false false) + (dwsh/relocate-shapes (into #{} (-> selected rest reverse)) variant-id 0) + (dwsh/update-shapes selected #(-> % + (assoc :constraints-h :left) + (assoc :constraints-v :top) + (assoc :fixed-scroll false))) + (dwsh/relocate-shapes #{variant-id} common-parent index) + (dwt/update-dimensions [variant-id] :width (+ (:width rect) 60)) + (dwt/update-dimensions [variant-id] :height (+ (:height rect) 60))) - ;; NOTE: we need to schedule a commit into a - ;; microtask for ensure that all the scheduled - ;; microtask of previous events execute before the - ;; commit - - (->> (rx/of (dwu/commit-undo-transaction undo-id)) - (rx/observe-on :async))))) + ;; NOTE: we need to schedule a commit into a + ;; microtask for ensure that all the scheduled + ;; microtask of previous events execute before the + ;; commit + (->> (rx/of (dwu/commit-undo-transaction undo-id)) + (rx/observe-on :async))))))) redirect-to-page (fn [page-id] diff --git a/frontend/src/app/main/ui/ds/product/milestone.scss b/frontend/src/app/main/ui/ds/product/milestone.scss index 016bc14ce6..b4ec7ab7ab 100644 --- a/frontend/src/app/main/ui/ds/product/milestone.scss +++ b/frontend/src/app/main/ui/ds/product/milestone.scss @@ -12,7 +12,6 @@ border: $b-1 solid var(--border-color, transparent); border-radius: $br-8; - cursor: pointer; background: var(--color-background-primary); display: grid; @@ -74,3 +73,7 @@ display: flex; padding-right: var(--sp-xs); } + +.menu-button { + cursor: pointer; +} diff --git a/frontend/src/app/main/ui/ds/product/milestone_group.scss b/frontend/src/app/main/ui/ds/product/milestone_group.scss index 479fe60877..0ed02e1f16 100644 --- a/frontend/src/app/main/ui/ds/product/milestone_group.scss +++ b/frontend/src/app/main/ui/ds/product/milestone_group.scss @@ -12,7 +12,6 @@ border: $b-1 solid var(--border-color, transparent); border-radius: $br-8; - cursor: pointer; background: var(--color-background-primary); display: grid; diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.cljs b/frontend/src/app/main/ui/inspect/right_sidebar.cljs index 649a532d6f..4c25d5c3cc 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.cljs +++ b/frontend/src/app/main/ui/inspect/right_sidebar.cljs @@ -8,15 +8,18 @@ (:require-macros [app.main.style :as stl]) (:require [app.common.types.component :as ctk] + [app.config :as cf] [app.main.data.event :as ev] [app.main.refs :as refs] [app.main.store :as st] + [app.main.ui.ds.controls.select :refer [select*]] [app.main.ui.ds.foundations.assets.icon :refer [icon*]] [app.main.ui.ds.layout.tab-switcher :refer [tab-switcher*]] [app.main.ui.icons :as i] [app.main.ui.inspect.attributes :refer [attributes]] [app.main.ui.inspect.code :refer [code]] [app.main.ui.inspect.selection-feedback :refer [resolve-shapes]] + [app.main.ui.inspect.styles :refer [styles-tab*]] [app.util.dom :as dom] [app.util.i18n :refer [tr]] [app.util.shape-icon :as usi] @@ -39,11 +42,13 @@ (mf/defc right-sidebar [{:keys [frame page objects file selected shapes page-id file-id share-id from on-change-section on-expand] :or {from :viewer}}] - (let [section (mf/use-state #(do :info)) + (let [color-space* (mf/use-state "hex") + color-space (deref color-space*) + + section (mf/use-state #(if (contains? cf/flags :inspect-styles) :styles :info)) objects (or objects (:objects page)) shapes (or shapes (resolve-shapes objects selected)) - first-shape (first shapes) page-id (or page-id (:id page)) file-id (or file-id (:id file)) @@ -82,20 +87,42 @@ (fn [] (dom/open-new-window "https://help.penpot.app/user-guide/inspect/"))) + handle-change-color-space + (mf/use-fn + (fn [color-space] + (reset! color-space* color-space))) + + color-spaces + (mf/with-memo [] + [{:label (tr "inspect.attributes.color.hex") + :id "hex"} + {:label (tr "inspect.attributes.color.rgba") + :id "rgba"} + {:label (tr "inspect.attributes.color.hsla") + :id "hsla"}]) + tabs (mf/with-memo [] - [{:label (tr "inspect.tabs.info") - :id "info"} - {:label (tr "inspect.tabs.code") - :data-testid "code" - :id "code"}])] + (if (contains? cf/flags :inspect-styles) + [{:label (tr "inspect.tabs.styles") + :id "styles"} + {:label (tr "inspect.tabs.computed") + :id "computed"} + {:label (tr "inspect.tabs.code") + :data-testid "code" + :id "code"}] + [{:label (tr "inspect.tabs.info") + :id "info"} + {:label (tr "inspect.tabs.code") + :data-testid "code" + :id "code"}]))] (mf/use-effect (mf/deps shapes handle-change-tab) (fn [] (if (seq shapes) (st/emit! (ptk/event ::ev/event {::ev/name "inspect-mode-click-element"})) - (handle-change-tab :info)))) + (handle-change-tab (if (contains? cf/flags :inspect-styles) :styles :info))))) [:aside {:class (stl/css-case :settings-bar-right true :viewer-code (= from :viewer))} @@ -133,27 +160,63 @@ [:div {:class (stl/css :layer-title)} (:name first-shape)]])]])] [:div {:class (stl/css :inspect-content)} + (if (contains? cf/flags :inspect-styles) + [:div {:class (stl/css :inspect-tab-switcher)} + [:span {:class (stl/css :inspect-tab-switcher-label)} (tr "inspect.tabs.switcher.label")] + [:div {:class (stl/css :inspect-tab-switcher-controls)} + [:div {:class (stl/css :inspect-tab-switcher-controls-color-space)} + [:> select* {:options color-spaces + :default-selected "hex" + :on-change handle-change-color-space}]] + [:div {:class (stl/css :inspect-tab-switcher-controls-tab)} + [:> select* {:options tabs + :default-selected (name @section) + :on-change handle-change-tab}]]]] + nil) - [:> tab-switcher* {:tabs tabs - :selected (name @section) - :on-change handle-change-tab - :class (stl/css :viewer-tab-switcher)} - (case @section - :info - [:& attributes {:page-id page-id - :objects objects - :file-id file-id - :frame frame - :shapes shapes - :from from - :libraries libraries - :share-id share-id}] + (if (contains? cf/flags :inspect-styles) + [:div {:class (stl/css :inspect-tab :viewer-tab-switcher :viewer-tab-switcher-layout)} + (case @section + :styles + [:> styles-tab* {:color-space color-space + :shapes shapes + :libraries libraries + :file-id file-id}] + :computed + [:& attributes {:page-id page-id + :objects objects + :file-id file-id + :frame frame + :shapes shapes + :from from + :libraries libraries + :share-id share-id}] - :code - [:& code {:frame frame - :shapes shapes - :on-expand handle-expand - :from from}])]]] + :code + [:& code {:frame frame + :shapes shapes + :on-expand handle-expand + :from from}])] + [:> tab-switcher* {:tabs tabs + :selected (name @section) + :on-change handle-change-tab + :class (stl/css :viewer-tab-switcher)} + (case @section + :info + [:& attributes {:page-id page-id + :objects objects + :file-id file-id + :frame frame + :shapes shapes + :from from + :libraries libraries + :share-id share-id}] + + :code + [:& code {:frame frame + :shapes shapes + :on-expand handle-expand + :from from}])])]] [:div {:class (stl/css :empty)} [:div {:class (stl/css :code-info)} [:span {:class (stl/css :placeholder-icon)} diff --git a/frontend/src/app/main/ui/inspect/right_sidebar.scss b/frontend/src/app/main/ui/inspect/right_sidebar.scss index ca3815ef96..a222bf7dd2 100644 --- a/frontend/src/app/main/ui/inspect/right_sidebar.scss +++ b/frontend/src/app/main/ui/inspect/right_sidebar.scss @@ -4,6 +4,7 @@ // // Copyright (c) KALEIDOS INC +@use "../ds/typography.scss" as *; @import "refactor/common-refactor.scss"; .settings-bar-right { @@ -106,6 +107,34 @@ padding: $s-8 $s-24; } +.inspect-tab-switcher { + display: flex; + justify-content: space-between; + align-items: center; + padding-block: var(--sp-s); + padding-inline-end: var(--sp-m); +} + +.inspect-tab-switcher-label { + @include use-typography("body-medium"); + flex: 1; +} + +.inspect-tab-switcher-controls { + display: flex; + align-items: center; + flex: 2; + gap: var(--sp-s); +} + +.inspect-tab-switcher-controls-color-space { + flex: 1; +} + +.inspect-tab-switcher-controls-tab { + flex: 2; +} + .inspect-content { flex: 1; overflow: hidden; @@ -115,3 +144,7 @@ --tabs-nav-padding-inline-start: 0; --tabs-nav-padding-inline-end: var(--sp-m); } + +.viewer-tab-switcher-layout { + display: grid; +} diff --git a/frontend/src/app/main/ui/inspect/styles.cljs b/frontend/src/app/main/ui/inspect/styles.cljs new file mode 100644 index 0000000000..2b9417f9b7 --- /dev/null +++ b/frontend/src/app/main/ui/inspect/styles.cljs @@ -0,0 +1,61 @@ +(ns app.main.ui.inspect.styles + (:require-macros [app.main.style :as stl]) + (:require + [app.common.data.macros :as dm] + [app.common.types.component :as ctc] + [app.common.types.components-list :as ctkl] + [app.main.ui.inspect.styles.style-box :refer [style-box*]] + [rumext.v2 :as mf])) + + +(def type->options + {:multiple [:fill :stroke :text :shadow :blur :layout-element] + :frame [:visibility :geometry :fill :stroke :shadow :blur :layout :layout-element] + :group [:visibility :geometry :svg :layout-element] + :rect [:visibility :geometry :fill :stroke :shadow :blur :svg :layout-element] + :circle [:visibility :geometry :fill :stroke :shadow :blur :svg :layout-element] + :path [:visibility :geometry :fill :stroke :shadow :blur :svg :layout-element] + :text [:visibility :geometry :text :shadow :blur :stroke :layout-element] + :variant [:variant :geometry :fill :stroke :shadow :blur :layout :layout-element]}) + +(defn- get-shape-type + [shapes first-shape first-component] + (cond + (and (= (count shapes) 1) + (or (ctc/is-variant-container? first-shape) + (ctc/is-variant? first-component))) + :variant + + (= (count shapes) 1) + (:type first-shape) + + :else + :multiple)) + +(mf/defc styles-tab* + [{:keys [color-space shapes libraries file-id]}] + (let [data (dm/get-in libraries [file-id :data]) + first-shape (first shapes) + first-component (ctkl/get-component data (:component-id first-shape)) + type (get-shape-type shapes first-shape first-component) + options (type->options type)] + + [:div {:class (stl/css :element-options)} + (for [[idx option] (map-indexed vector options)] + [:> style-box* {:key idx :attribute option} color-space])])) + + +;; WIP +;; Panel list as stylebox children +#_(case option + :geometry [:> geometry-panel {}] + :layout [:> layout-panel {}] + :layout-element [:> layout-element-panel {}] + :fill [:> fill-panel {:color-space color-space}] + :stroke [:> stroke-panel {:color-space color-space}] + :text [:> text-panel {:color-space color-space}] + :shadow [:> shadow-panel {}] + :blur [:> blur-panel {}] + :svg [:> svg-panel {}] + :variant [:> variant-panel* {}] + :visibility [:> visibility-panel* {}]) diff --git a/frontend/src/app/main/ui/inspect/styles/style_box.cljs b/frontend/src/app/main/ui/inspect/styles/style_box.cljs new file mode 100644 index 0000000000..8e0956bb81 --- /dev/null +++ b/frontend/src/app/main/ui/inspect/styles/style_box.cljs @@ -0,0 +1,26 @@ +(ns app.main.ui.inspect.styles.style-box + (:require-macros [app.main.style :as stl]) + (:require + [rumext.v2 :as mf])) + +(defn- attribute->title + [type] + (case type + :variant "Variant properties" + :token "Token Sets & Themes" + :geometry "Size & Position" + :fill "Fill" + :stroke "Stroke" + :text "Text" + :shadow "Shadow" + :layout "Layout" + :layout-element "Layout Element" + :visibility "Visibility" + :svg "SVG" + nil)) + +(mf/defc style-box* + [{:keys [attribute children]}] + [:div {:class (stl/css :style-box)} + [:h3 {:class (stl/css :style-box-header)} (attribute->title attribute)] + [:div {:class (stl/css :style-box-content)} children]]) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs index 42cefd25a9..d368e46e33 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -460,7 +460,7 @@ (st/emit! (dwu/start-undo-transaction undo-id)) (run! st/emit! (->> components - (filter #(str/starts-with? (:path %) path)) + (filter #(cfh/inside-path? (:path %) path)) (map #(dwv/rename-comp-or-variant-and-main (:id %) (cmm/rename-group % path last-path))))) @@ -491,7 +491,7 @@ (st/emit! (dwu/start-undo-transaction undo-id)) (run! st/emit! (->> components - (filter #(str/starts-with? (:path %) path)) + (filter #(cfh/inside-path? (:path %) path)) (map #(dwv/rename-comp-or-variant-and-main (:id %) (cmm/ungroup % path))))) (st/emit! (dwu/commit-undo-transaction undo-id))))) @@ -501,7 +501,7 @@ (fn [path] (on-clear-selection) (let [comps (->> components - (filter #(str/starts-with? (:path %) path))) + (filter #(cfh/inside-path? (:path %) path))) ids (into #{} (map :main-instance-id comps)) page-id (->> comps first :main-instance-page)] diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 76ebfa10f8..cf2324fa49 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1761,8 +1761,12 @@ msgid "inspect.subtitle.main" msgstr "Main component" #: src/app/main/ui/inspect/right_sidebar.cljs:59 -msgid "inspect.subtitle.variant" -msgstr "Variant" +msgid "inspect.tabs.switcher.label" +msgstr "Layer info" + +#: src/app/main/ui/inspect/right_sidebar.cljs:101 +msgid "inspect.tabs.computed" +msgstr "Computed" #: src/app/main/ui/inspect/right_sidebar.cljs:105 msgid "inspect.tabs.code" @@ -1820,6 +1824,10 @@ msgstr "Text" msgid "inspect.tabs.info" msgstr "Info" +#: src/app/main/ui/inspect/right_sidebar.cljs:101 +msgid "inspect.tabs.styles" +msgstr "Styles" + #: src/app/main/ui/dashboard/comments.cljs:95 msgid "label.mark-all-as-read" msgstr "Mark all as read" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 44aced0c81..6f7e31f7d4 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1768,6 +1768,14 @@ msgstr "Componente principal" msgid "inspect.subtitle.variant" msgstr "Variante" +#: src/app/main/ui/inspect/right_sidebar.cljs:59 +msgid "inspect.tabs.switcher.label" +msgstr "Información sobre la capa" + +#: src/app/main/ui/inspect/right_sidebar.cljs:101 +msgid "inspect.tabs.computed" +msgstr "Calculado" + #: src/app/main/ui/inspect/right_sidebar.cljs:105 msgid "inspect.tabs.code" msgstr "Código" @@ -1824,6 +1832,10 @@ msgstr "Texto" msgid "inspect.tabs.info" msgstr "Información" +#: src/app/main/ui/inspect/right_sidebar.cljs:101 +msgid "inspect.tabs.styles" +msgstr "Estilos" + #: src/app/main/ui/dashboard/comments.cljs:95 msgid "label.mark-all-as-read" msgstr "Marcar todo como leído"