From b6c4376217c08eeba115ac7fda0d250c20f546cf Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Fri, 28 Mar 2025 11:20:10 +0100 Subject: [PATCH] :bug: Fix bugs from varaints design review --- common/src/app/common/files/repair.cljc | 10 +++ common/src/app/common/files/validate.cljc | 23 +++++- common/src/app/common/files/variant.cljc | 20 +++++ common/src/app/common/logic/shapes.cljc | 4 +- common/src/app/common/types/variant.cljc | 2 +- .../common_tests/logic/variants_test.cljc | 2 +- frontend/src/app/main/data/workspace.cljs | 75 +++++++++++------- .../app/main/data/workspace/shape_layout.cljs | 5 +- .../src/app/main/data/workspace/variants.cljs | 78 ++++++++++++++++++- .../main/ui/inspect/attributes/variant.cljs | 4 +- .../workspace/sidebar/assets/components.cljs | 39 ++++++---- .../workspace/sidebar/assets/components.scss | 27 +++++++ .../sidebar/assets/file_library.cljs | 18 ++++- .../main/ui/workspace/sidebar/layer_item.cljs | 33 ++++---- .../sidebar/options/menus/component.scss | 1 + .../main/ui/workspace/viewport/outline.cljs | 5 +- .../main/ui/workspace/viewport/selection.cljs | 9 ++- .../main/ui/workspace/viewport/widgets.cljs | 2 +- frontend/src/app/plugins/library.cljs | 3 +- frontend/translations/en.po | 6 ++ frontend/translations/es.po | 6 ++ 21 files changed, 286 insertions(+), 86 deletions(-) diff --git a/common/src/app/common/files/repair.cljc b/common/src/app/common/files/repair.cljc index a99307ee1..bf5185100 100644 --- a/common/src/app/common/files/repair.cljc +++ b/common/src/app/common/files/repair.cljc @@ -607,6 +607,16 @@ (log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error)) file) +(defmethod repair-error :variant-bad-variant-name + [_ error file _] + (log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error)) + file) + +(defmethod repair-error :variant-component-bad-name + [_ error file _] + (log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error)) + file) + (defmethod repair-error :default [_ error file _] (log/error :hint "Unknown error code, don't know how to repair" :code (:code error)) diff --git a/common/src/app/common/files/validate.cljc b/common/src/app/common/files/validate.cljc index ea7777cdd..982f27aaa 100644 --- a/common/src/app/common/files/validate.cljc +++ b/common/src/app/common/files/validate.cljc @@ -66,6 +66,8 @@ :variant-not-main :parent-not-variant :variant-bad-name + :variant-bad-variant-name + :variant-component-bad-name :variant-no-properties}) (def ^:private schema:error @@ -414,7 +416,8 @@ (defn- check-variant-container "Shape is a variant container, so: -all its children should be variants with variant-id equals to the shape-id - -all the components should have the same properties" + -all the components should have the same properties + " [shape file page] (let [shape-id (:id shape) shapes (:shapes shape) @@ -439,7 +442,9 @@ "Shape is a variant, so -it should be a main component -its parent should be a variant-container - -its variant-name is derived from the properties" + -its variant-name is derived from the properties + -its name should be tha same as its parent's + " [shape file page] (let [parent (ctst/get-shape page (:parent-id shape)) component (ctkl/get-component (:data file) (:component-id shape) true) @@ -454,8 +459,16 @@ shape file page)) (when-not (= name (:variant-name shape)) - (report-error :variant-bad-name + (report-error :variant-bad-variant-name (str/ffmt "Variant % has an invalid variant-name" (:id shape)) + shape file page)) + (when-not (= (:name parent) (:name shape)) + (report-error :variant-bad-name + (str/ffmt "Variant % has an invalid name" (:id shape)) + shape file page)) + (when-not (= (:name parent) (cfh/merge-path-item (:path component) (:name component))) + (report-error :variant-component-bad-name + (str/ffmt "Component % has an invalid name" (:id shape)) shape file page)))) (defn- check-shape @@ -565,7 +578,9 @@ -It should have at least one variant property" [component file] (let [component-page (ctf/get-component-page (:data file) component) - main-component (ctst/get-shape component-page (:main-instance-id component))] + main-component (if (:deleted component) + (dm/get-in component [:objects (:main-instance-id component)]) + (ctst/get-shape component-page (:main-instance-id component)))] (when-not (ctk/is-variant? main-component) (report-error :not-a-variant (str/ffmt "Shape % should be a variant" (:id main-component)) diff --git a/common/src/app/common/files/variant.cljc b/common/src/app/common/files/variant.cljc index 226929768..86c0e8283 100644 --- a/common/src/app/common/files/variant.cljc +++ b/common/src/app/common/files/variant.cljc @@ -6,6 +6,7 @@ (ns app.common.files.variant (:require [app.common.data.macros :as dm] + [app.common.types.component :as ctc] [app.common.types.components-list :as ctcl] [app.common.types.variant :as ctv] [cuerdas.core :as str])) @@ -61,3 +62,22 @@ (and (seq shapes) (not= (:main-instance-id component) (last shapes))))) +(defn get-primary-variant + [data component] + (let [page-id (:main-instance-page component) + objects (-> (dm/get-in data [:pages-index page-id]) + (get :objects)) + variant-id (:variant-id component)] + (->> (dm/get-in objects [variant-id :shapes]) + peek + (get objects)))) + +(defn get-primary-component + [data component-id] + (when-let [component (ctcl/get-component data component-id)] + (if (ctc/is-variant? component) + (->> component + (get-primary-variant data) + :component-id + (ctcl/get-component data)) + component))) diff --git a/common/src/app/common/logic/shapes.cljc b/common/src/app/common/logic/shapes.cljc index 94faa7b82..4473e60b1 100644 --- a/common/src/app/common/logic/shapes.cljc +++ b/common/src/app/common/logic/shapes.cljc @@ -256,7 +256,7 @@ child-heads-ids (map :id child-heads) - variant-heads (filter ctk/is-variant? child-heads) + variant-shapes (filter ctk/is-variant? shapes) component-main-parent (ctn/find-component-main objects parent false) @@ -384,7 +384,7 @@ #(-> (dissoc % :variant-id :variant-name) (assoc :name new-name)))))) changes - variant-heads)))) + variant-shapes)))) ;; Add variant info and rename when moving into a different variant-container (cond-> (ctk/is-variant-container? parent) diff --git a/common/src/app/common/types/variant.cljc b/common/src/app/common/types/variant.cljc index 9f5eded0a..fddf670cb 100644 --- a/common/src/app/common/types/variant.cljc +++ b/common/src/app/common/types/variant.cljc @@ -51,7 +51,7 @@ (def property-prefix "Property") (def property-regex (re-pattern (str property-prefix "(\\d+)"))) -(def value-prefix "Value") +(def value-prefix "Value ") (defn properties-to-name diff --git a/common/test/common_tests/logic/variants_test.cljc b/common/test/common_tests/logic/variants_test.cljc index 8f6eeb0a4..2c63a3e61 100644 --- a/common/test/common_tests/logic/variants_test.cljc +++ b/common/test/common_tests/logic/variants_test.cljc @@ -117,7 +117,7 @@ (t/is (= (count (:variant-properties comp01')) 2)) (t/is (= (count (:variant-properties comp02)) 1)) (t/is (= (count (:variant-properties comp02')) 2)) - (t/is (= (-> comp01' :variant-properties last :value) "Value1")))) + (t/is (= (-> comp01' :variant-properties last :value) "Value 1")))) diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index ab961070d..43937ac45 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -13,6 +13,7 @@ [app.common.features :as cfeat] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] + [app.common.files.variant :as cfv] [app.common.geom.align :as gal] [app.common.geom.point :as gpt] [app.common.geom.proportions :as gpp] @@ -25,7 +26,7 @@ [app.common.schema :as sm] [app.common.text :as txt] [app.common.transit :as t] - [app.common.types.component :as ctk] + [app.common.types.component :as ctc] [app.common.types.components-list :as ctkl] [app.common.types.container :as ctn] [app.common.types.file :as ctf] @@ -75,6 +76,7 @@ [app.main.data.workspace.thumbnails :as dwth] [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] + [app.main.data.workspace.variants :as dwva] [app.main.data.workspace.viewport :as dwv] [app.main.data.workspace.zoom :as dwz] [app.main.errors] @@ -577,7 +579,7 @@ name (cfh/generate-unique-name base-name unames :suffix-fn suffix-fn) objects (update-vals (:objects page) #(dissoc % :use-for-thumbnail)) - main-instances-ids (set (keep #(when (ctk/main-instance? (val %)) (key %)) objects)) + main-instances-ids (set (keep #(when (ctc/main-instance? (val %)) (key %)) objects)) ids-to-remove (set (apply concat (map #(cfh/get-children-ids objects %) main-instances-ids))) add-component-copy @@ -794,27 +796,36 @@ ([] (end-rename-shape nil nil)) ([shape-id name] (ptk/reify ::end-rename-shape + ptk/UpdateEvent + (update [_ state] + ;; Remove rename state from workspace local state + (update state :workspace-local dissoc :shape-for-rename)) ptk/WatchEvent (watch [_ state _] (when-let [shape-id (d/nilv shape-id (dm/get-in state [:workspace-local :shape-for-rename]))] - (let [shape (dsh/lookup-shape state shape-id) - name (str/trim name) - clean-name (cfh/clean-path name) - valid? (and (not (str/ends-with? name "/")) - (string? clean-name) - (not (str/blank? clean-name)))] - (rx/concat - ;; Remove rename state from workspace local state - (rx/of #(update % :workspace-local dissoc :shape-for-rename)) + (let [shape (dsh/lookup-shape state shape-id) + name (str/trim name) + clean-name (cfh/clean-path name) + valid? (and (not (str/ends-with? name "/")) + (string? clean-name) + (not (str/blank? clean-name))) + component-id (:component-id shape) + undo-id (js/Symbol)] - ;; Rename the shape if string is not empty/blank - (when valid? - (rx/of (update-shape shape-id {:name clean-name}))) - ;; Update the component in case if shape is a main instance - (when (and valid? (:main-instance shape)) - (when-let [component-id (:component-id shape)] - (rx/of (dwl/rename-component component-id clean-name))))))))))) + (when valid? + (if (ctc/is-variant-container? shape) + ;; Rename the full variant when it is a variant container + (rx/of (dwva/rename-variant shape-id clean-name)) + (rx/of + (dwu/start-undo-transaction undo-id) + ;; Rename the shape if string is not empty/blank + (update-shape shape-id {:name clean-name}) + + ;; Update the component in case shape is a main instance + (when (and (some? component-id) (ctc/main-instance? shape)) + (dwl/rename-component component-id clean-name)) + (dwu/commit-undo-transaction undo-id)))))))))) ;; --- Update Selected Shapes attrs @@ -1206,22 +1217,26 @@ (ptk/reify ::show-component-in-assets ptk/WatchEvent (watch [_ state _] - (let [file-id (:current-file-id state) - fdata (dsh/lookup-file-data state file-id) - cpath (dm/get-in fdata [:components component-id :path]) - cpath (cfh/split-path cpath) - paths (map (fn [i] (cfh/join-path (take (inc i) cpath))) - (range (count cpath)))] + (let [file-id (:current-file-id state) + fdata (dsh/lookup-file-data state file-id) + component (cfv/get-primary-component fdata component-id) + cpath (:path component) + cpath (cfh/split-path cpath) + paths (map (fn [i] (cfh/join-path (take (inc i) cpath))) + (range (count cpath)))] (rx/concat (rx/from (map #(set-assets-group-open file-id :components % true) paths)) (rx/of (dcm/go-to-workspace :layout :assets) (set-assets-section-open file-id :library true) (set-assets-section-open file-id :components true) - (select-single-asset file-id component-id :components))))) + (select-single-asset file-id (:id component) :components))))) ptk/EffectEvent - (effect [_ _ _] - (let [wrapper-id (str "component-shape-id-" component-id)] + (effect [_ state _] + (let [file-id (:current-file-id state) + fdata (dsh/lookup-file-data state file-id) + component (cfv/get-primary-component fdata component-id) + wrapper-id (str "component-shape-id-" (:id component))] (tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-id))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -1388,7 +1403,7 @@ heads)))) (advance-copy [file libraries page objects shape] - (if (and (ctk/instance-head? shape) (not (ctk/main-instance? shape))) + (if (and (ctc/instance-head? shape) (not (ctc/main-instance? shape))) (let [level-delta (ctn/get-nesting-level-delta (:objects page) shape uuid/zero)] (if (pos? level-delta) (reduce (partial advance-shape file libraries page level-delta) @@ -2118,7 +2133,7 @@ undo-id (js/Symbol)] (rx/concat - (->> (filter ctk/instance-head? orig-shapes) + (->> (filter ctc/instance-head? orig-shapes) (map (fn [{:keys [component-file]}] (ptk/event ::ev/event {::ev/name "use-library-component" @@ -2433,7 +2448,7 @@ (let [objects (dsh/lookup-page-objects state) copies (->> objects vals - (filter #(and (ctk/instance-head? %) (not (ctk/main-instance? %))))) + (filter #(and (ctc/instance-head? %) (not (ctc/main-instance? %))))) copies-no-ref (filter #(not (:shape-ref %)) copies) find-childs-no-ref (fn [acc-map item] diff --git a/frontend/src/app/main/data/workspace/shape_layout.cljs b/frontend/src/app/main/data/workspace/shape_layout.cljs index ffabffbe8..6436596b1 100644 --- a/frontend/src/app/main/data/workspace/shape_layout.cljs +++ b/frontend/src/app/main/data/workspace/shape_layout.cljs @@ -242,12 +242,13 @@ selected-shapes (map (d/getf objects) selected) single? (= (count selected-shapes) 1) is-frame? (= :frame (:type (first selected-shapes))) - is-variant-cont? (ctc/is-variant-container? (first selected-shapes)) + has-layout? (ctl/any-layout? (first selected-shapes)) + undo-id (js/Symbol)] (rx/of (dwu/start-undo-transaction undo-id) - (if (and single? is-frame? (not is-variant-cont?)) + (if (and single? is-frame? (not has-layout?)) (create-layout-from-id (first selected) type :from-frame? true) (create-layout-from-selection type)) (dwu/commit-undo-transaction undo-id)))))) diff --git a/frontend/src/app/main/data/workspace/variants.cljs b/frontend/src/app/main/data/workspace/variants.cljs index b09ff188b..08c5fa985 100644 --- a/frontend/src/app/main/data/workspace/variants.cljs +++ b/frontend/src/app/main/data/workspace/variants.cljs @@ -10,10 +10,12 @@ [app.common.data :as d] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] + [app.common.files.variant :as cfv] [app.common.logic.variant-properties :as clvp] [app.common.logic.variants :as clv] [app.common.types.component :as ctc] [app.common.types.components-list :as ctkl] + [app.common.types.shape.layout :as ctsl] [app.common.uuid :as uuid] [app.main.data.changes :as dch] [app.main.data.helpers :as dsh] @@ -22,6 +24,7 @@ [app.main.data.workspace.selection :as dws] [app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shapes :as dwsh] + [app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.undo :as dwu] [app.main.features :as features] [app.util.dom :as dom] @@ -144,6 +147,25 @@ (dom/focus! (dom/get-element (str "variant-prop-" shape-id prop-num)))))) +(defn- resposition-and-resize-variant + "Resize the variant container, and move the shape (that is a variant) to the right" + [shape-id] + (ptk/reify ::resposition-and-resize-variant + ptk/WatchEvent + (watch [_ state _] + (let [page-id (:current-page-id state) + objects (dsh/lookup-page-objects state page-id) + shape (get objects shape-id) + container (get objects (:parent-id shape)) + width (+ (:width container) (:width shape) 20) ;; 20 is the default gap for variants + x (- width (+ (:width shape) 30))] ;; 30 is the default margin for variants + (rx/of + (dwt/update-dimensions [(:parent-id shape)] :width width) + (dwt/update-position shape-id + {:x x} + {:absolute? false})))))) + + (defn add-new-variant "Create a new variant and add it to the variant-container" [shape-id] @@ -161,6 +183,10 @@ component-id (:component-id shape) component (ctkl/get-component data component-id) + container-id (:parent-id shape) + variant-container (get objects container-id) + has-layout? (ctsl/any-layout? variant-container) + new-component-id (uuid/next) new-shape-id (uuid/next) @@ -177,6 +203,8 @@ (rx/of (dwu/start-undo-transaction undo-id) (dch/commit-changes changes) + (when-not has-layout? + (resposition-and-resize-variant new-shape-id)) (dwu/commit-undo-transaction undo-id) (ptk/data-event :layout/update {:ids [(:parent-id shape)]}) (dws/select-shape new-shape-id)) @@ -196,7 +224,7 @@ page-id (:current-page-id state) objects (dsh/lookup-page-objects state file-id page-id) main (get objects main-instance-id) - main-id (:id main) + parent (get objects (:parent-id main)) component-id (:component-id main) cpath (cfh/split-path (:name main)) name (first cpath) @@ -237,9 +265,16 @@ (cl/remove-all-fills variant-vec {:color clr/black :opacity 1}) (dwsl/create-layout-from-id variant-id :flex) (dwsh/update-shapes variant-vec #(merge % cont-props)) - (dwsh/update-shapes [main-id] #(merge % main-props)) + (dwsh/update-shapes [main-instance-id] #(merge % main-props)) (cl/add-stroke variant-vec stroke-props) - (set-variant-id component-id variant-id)) + (set-variant-id component-id variant-id) + + ;; Set the position of the variant container so the main shape doesn't + ;; change its position + (when-not (ctsl/any-layout? parent) + (dwt/update-position variant-id + {:x (- (:x main) 30) :y (- (:y main) 30)} + {:absolute? true}))) ;; Add the necessary number of new properties, with default values (rx/from @@ -310,3 +345,40 @@ (rx/from (map add-new-variant selected-ids)) (rx/of (dwu/commit-undo-transaction undo-id))) (rx/of (dws/duplicate-selected true))))))) + + +(defn rename-variant + [variant-id name] + (ptk/reify ::rename-variant + + ptk/WatchEvent + (watch [_ state _] + (let [page-id (:current-page-id state) + data (dsh/lookup-file-data state) + objects (-> (dsh/get-page data page-id) + (get :objects)) + variant-components (cfv/find-variant-components data objects variant-id) + clean-name (cfh/clean-path name) + undo-id (js/Symbol)] + + (rx/concat + (rx/of (dwu/start-undo-transaction undo-id) + (dwsh/update-shapes [variant-id] #(assoc % :name clean-name))) + (rx/from (map + #(dwl/rename-component-and-main-instance (:id %) clean-name) + variant-components)) + (rx/of (dwu/commit-undo-transaction undo-id))))))) + + +(defn rename-comp-or-variant-and-main + [component-id name] + (ptk/reify ::rename-comp-or-variant-and-main + + ptk/WatchEvent + (watch [_ state _] + (let [data (dsh/lookup-file-data state) + component (ctkl/get-component data component-id)] + (if (ctc/is-variant? component) + (rx/of (rename-variant (:variant-id component) name)) + (rx/of (dwl/rename-component-and-main-instance component-id name))))))) + diff --git a/frontend/src/app/main/ui/inspect/attributes/variant.cljs b/frontend/src/app/main/ui/inspect/attributes/variant.cljs index 13855a19a..e9b74dad0 100644 --- a/frontend/src/app/main/ui/inspect/attributes/variant.cljs +++ b/frontend/src/app/main/ui/inspect/attributes/variant.cljs @@ -30,9 +30,9 @@ (mf/defc variant-panel* [{:keys [objects shapes libraries file-id] :as kk}] (let [shape (->> shapes first) + is-container? (ctc/is-variant-container? shape) properties (mf/with-memo [objects shape] (let [data (dm/get-in libraries [file-id :data]) - is-container? (ctc/is-variant-container? shape) component (when-not is-container? (ctkl/get-component data (:component-id shape)))] (if is-container? (->> (cfv/extract-properties-values data objects (:id shape)) @@ -41,7 +41,7 @@ (map #(update % :value (fn [v] (if (str/blank? v) "--" v))))))))] [:div {:class (stl/css :attributes-block)} [:> inspect-title-bar* - {:title (tr "inspect.attributes.variant") + {:title (if is-container? (tr "inspect.attributes.variants") (tr "inspect.attributes.variant")) :class (stl/css :title-spacing-variant)}] (for [[pos property] (map-indexed vector properties)] 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 6ba863d7b..277c4d3cf 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.media :as dwm] [app.main.data.workspace.undo :as dwu] + [app.main.data.workspace.variants :as dwv] [app.main.refs :as refs] [app.main.store :as st] [app.main.ui.components.editable-label :refer [editable-label]] @@ -54,7 +55,7 @@ {::mf/wrap-props false} [{:keys [component renaming listing-thumbs? selected file-id on-asset-click on-context-menu on-drag-start do-rename - cancel-rename selected-full selected-paths is-local]}] + cancel-rename selected-full selected-paths is-local num-variants]}] (let [item-ref (mf/use-ref) @@ -95,7 +96,7 @@ (fn [event] (when (and is-local (:is-local @drag-data*)) (cmm/on-drop-asset event component dragging* selected selected-full - selected-paths dwl/rename-component-and-main-instance)))) + selected-paths dwv/rename-comp-or-variant-and-main)))) on-drag-enter (mf/use-fn @@ -129,7 +130,8 @@ [:div {:ref item-ref :class (stl/css-case :selected (contains? selected (:id component)) :grid-cell listing-thumbs? - :enum-item (not listing-thumbs?)) + :enum-item (not listing-thumbs?) + :enum-item-with-mark (and (not listing-thumbs?) (ctc/is-variant? component))) :id (dm/str "component-shape-id-" (:id component)) :draggable (and (not read-only?) (not renaming?)) :on-click on-component-click @@ -166,13 +168,16 @@ :root-shape root-shape :component component :container container - :is-hidden (not visible?)}]])])) + :is-hidden (not visible?)}] + (when (ctc/is-variant? component) + [:span {:class (stl/css-case :variant-mark-cell listing-thumbs? :variant-mark true :component-icon true) + :title (tr "workspace.assets.components.num-variants" num-variants)} i/variant])])])) (mf/defc components-group {::mf/wrap-props false} [{:keys [file-id prefix groups open-groups force-open? renaming listing-thumbs? selected on-asset-click on-drag-start do-rename cancel-rename on-rename-group on-group on-ungroup on-context-menu - selected-full is-local]}] + selected-full is-local count-variants]}] (let [group-open? (if (false? (get open-groups prefix)) ;; if the user has closed it specifically, respect that false @@ -205,7 +210,7 @@ (mf/deps dragging* prefix selected-paths selected-full is-local drag-data*) (fn [event] (when (and is-local (:is-local @drag-data*)) - (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full dwl/rename-component-and-main-instance))))] + (cmm/on-drop-asset-group event dragging* prefix selected-paths selected-full dwv/rename-comp-or-variant-and-main))))] [:div {:class (stl/css :component-group) :on-drag-enter on-drag-enter @@ -220,7 +225,6 @@ :on-rename on-rename-group :on-ungroup on-ungroup}] - (when group-open? [:* (let [components (not-empty (get groups "" []))] @@ -257,7 +261,8 @@ :on-group on-group :do-rename do-rename :cancel-rename cancel-rename - :is-local is-local}])]) + :is-local is-local + :num-variants (count-variants (:variant-id component))}])]) (for [[path-item content] groups] (when-not (empty? path-item) @@ -278,13 +283,14 @@ :on-ungroup on-ungroup :on-context-menu on-context-menu :selected-full selected-full - :is-local is-local}]))])])) + :is-local is-local + :count-variants count-variants}]))])])) (mf/defc components-section {::mf/wrap-props false} [{:keys [file-id is-local components listing-thumbs? open? force-open? reverse-sort? selected on-asset-click on-assets-delete - on-clear-selection open-status-ref]}] + on-clear-selection open-status-ref count-variants]}] (let [input-ref (mf/use-ref nil) @@ -379,7 +385,7 @@ (swap! state* dissoc :renaming) (when (not (str/blank? new-name)) (st/emit! - (dwl/rename-component-and-main-instance current-component-id new-name))))) + (dwv/rename-comp-or-variant-and-main current-component-id new-name))))) on-context-menu (mf/use-fn @@ -408,7 +414,7 @@ (filter #(if multi-components? (contains? selected (:id %)) (= current-component-id (:id %)))) - (map #(dwl/rename-component-and-main-instance + (map #(dwv/rename-comp-or-variant-and-main (:id %) (cmm/add-group % group-name))))) (st/emit! (dwu/commit-undo-transaction undo-id))))) @@ -423,7 +429,7 @@ (run! st/emit! (->> components (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-component-and-main-instance + (map #(dwv/rename-comp-or-variant-and-main (:id %) (cmm/rename-group % path last-path))))) (st/emit! (dwu/commit-undo-transaction undo-id))))) @@ -454,7 +460,7 @@ (run! st/emit! (->> components (filter #(str/starts-with? (:path %) path)) - (map #(dwl/rename-component-and-main-instance (:id %) (cmm/ungroup % path))))) + (map #(dwv/rename-comp-or-variant-and-main (:id %) (cmm/ungroup % path))))) (st/emit! (dwu/commit-undo-transaction undo-id))))) on-drag-start @@ -544,7 +550,8 @@ :on-ungroup on-ungroup :on-context-menu on-context-menu :selected-full selected-full - :is-local ^boolean is-local}]) + :is-local ^boolean is-local + :count-variants count-variants}]) [:& cmm/assets-context-menu {:on-close on-close-menu @@ -564,7 +571,7 @@ {:name (tr "workspace.assets.delete") :id "assets-delete-component" :handler on-delete}) - (when (and is-local (not (or multi-assets? read-only? any-variant?))) + (when (and is-local (not (or multi-assets? read-only?))) {:name (tr "workspace.assets.group") :id "assets-group-component" :handler on-group}) diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss index 934013902..5b38aab48 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/components.scss @@ -157,6 +157,10 @@ } } +.enum-item-with-mark { + grid-template-columns: auto 1fr $s-24; +} + .item-name { @include bodySmallTypography; @include textEllipsis; @@ -219,3 +223,26 @@ background-color: var(--assets-item-background-color-drag); border: $s-2 solid var(--assets-item-border-color-drag); } + +.variant-mark { + background-color: var(--color-background-tertiary); + border-radius: $br-8; +} + +.variant-mark-cell { + position: absolute; + right: $s-2; + top: $s-2; +} + +.component-icon { + @include flexCenter; + height: $s-24; + width: $s-24; + order: 3; + + svg { + @extend .button-icon-small; + stroke: var(--color-accent-secondary); + } +} diff --git a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs index b2ad36591..a2efced2f 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/assets/file_library.cljs @@ -145,7 +145,7 @@ (mf/defc file-library-content* {::mf/private true} - [{:keys [file is-local is-loaded open-status-ref on-clear-selection filters colors media typographies components]}] + [{:keys [file is-local is-loaded open-status-ref on-clear-selection filters colors media typographies components count-variants]}] (let [open-status (mf/deref open-status-ref) file-id (:id file) @@ -263,7 +263,8 @@ :selected selected :on-asset-click on-component-click :on-assets-delete on-assets-delete - :on-clear-selection on-clear-selection}]) + :on-clear-selection on-clear-selection + :count-variants count-variants}]) (when ^boolean show-graphics? [:& graphics-section @@ -385,7 +386,15 @@ (mf/use-fn (mf/deps file-id) (fn [] - (st/emit! (dw/unselect-all-assets file-id))))] + (st/emit! (dw/unselect-all-assets file-id)))) + + count-variants + (mf/use-fn + (mf/deps library) + (fn [variant-id] + (->> (ctkl/components-seq library) + (filterv #(= variant-id (:variant-id %))) + count)))] [:div {:class (stl/css :tool-window) :on-context-menu dom/prevent-default @@ -409,4 +418,5 @@ :media filtered-media :typographies filtered-typographies :on-clear-selection unselect-all - :open-status-ref open-status-ref}])])) + :open-status-ref open-status-ref + :count-variants count-variants}])])) diff --git a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs index 81d1ef59d..1624071d6 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layer_item.cljs @@ -44,20 +44,21 @@ on-toggle-collapse on-enable-drag on-disable-drag on-toggle-visibility on-toggle-blocking]} dref] - (let [id (:id item) - name (:name item) - blocked? (:blocked item) - hidden? (:hidden item) - has-shapes? (-> item :shapes seq boolean) - touched? (-> item :touched seq boolean) - parent-board? (and (cfh/frame-shape? item) - (= uuid/zero (:parent-id item))) - absolute? (ctl/item-absolute? item) - components-v2 (mf/use-ctx ctx/components-v2) - main-instance? (or (not components-v2) (:main-instance item)) - variants? (features/use-feature "variants/v1") - is-variant? (when variants? (ctk/is-variant? item)) - variant-name (when is-variant? (:variant-name item))] + (let [id (:id item) + name (:name item) + blocked? (:blocked item) + hidden? (:hidden item) + has-shapes? (-> item :shapes seq boolean) + touched? (-> item :touched seq boolean) + parent-board? (and (cfh/frame-shape? item) + (= uuid/zero (:parent-id item))) + absolute? (ctl/item-absolute? item) + components-v2 (mf/use-ctx ctx/components-v2) + main-instance? (or (not components-v2) (:main-instance item)) + variants? (features/use-feature "variants/v1") + is-variant? (when variants? (ctk/is-variant? item)) + variant-name (when is-variant? (:variant-name item)) + is-variant-container? (when variants? (ctk/is-variant-container? item))] [:* [:div {:id id :ref dref @@ -72,7 +73,7 @@ :selected selected? :type-frame (cfh/frame-shape? item) :type-bool (cfh/bool-shape? item) - :type-comp component-tree? + :type-comp (or component-tree? is-variant-container?) :hidden hidden? :dnd-over dnd-over? :dnd-over-top dnd-over-top? @@ -132,7 +133,7 @@ :is-blocked blocked? :parent-size parent-size :is-selected selected? - :type-comp component-tree? + :type-comp (or component-tree? is-variant-container?) :type-frame (cfh/frame-shape? item) :variant-name variant-name :is-hidden hidden?}] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss index 4c210a876..e9e6c3943 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/component.scss @@ -714,6 +714,7 @@ width: 100%; display: flex; gap: var(--sp-xs); + padding-right: var(--sp-xxs); } .variant-property-name-bg { diff --git a/frontend/src/app/main/ui/workspace/viewport/outline.cljs b/frontend/src/app/main/ui/workspace/viewport/outline.cljs index 41b67bc99..0043a3fe8 100644 --- a/frontend/src/app/main/ui/workspace/viewport/outline.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/outline.cljs @@ -11,6 +11,7 @@ [app.common.exceptions :as ex] [app.common.files.helpers :as cfh] [app.common.geom.shapes :as gsh] + [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.main.refs :as refs] [app.main.ui.hooks :as hooks] @@ -32,7 +33,9 @@ ;; NOTE: that we don't use mf/deref to avoid a repaint dependency here objects (deref refs/workspace-page-objects) - color (if (ctn/in-any-component? objects shape) + color (if (or + (ctn/in-any-component? objects shape) + (ctk/is-variant-container? shape)) "var(--assets-component-hightlight)" "var(--color-accent-tertiary)") diff --git a/frontend/src/app/main/ui/workspace/viewport/selection.cljs b/frontend/src/app/main/ui/workspace/viewport/selection.cljs index 98ce5a047..a3b4005f0 100644 --- a/frontend/src/app/main/ui/workspace/viewport/selection.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/selection.cljs @@ -12,6 +12,7 @@ [app.common.geom.matrix :as gmt] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] + [app.common.types.component :as ctk] [app.common.types.container :as ctn] [app.common.types.shape :as cts] [app.main.data.workspace :as dw] @@ -530,7 +531,9 @@ ;; Note that we don't use mf/deref to avoid a repaint dependency here objects (deref refs/workspace-page-objects) - color (if (and (= total 1) ^boolean (ctn/in-any-component? objects shape)) + color (if (and (= total 1) ^boolean + (or (ctn/in-any-component? objects shape) + (ctk/is-variant-container? shape))) selection-rect-color-component selection-rect-color-normal)] @@ -577,7 +580,9 @@ ;; Note that we don't use mf/deref to avoid a repaint dependency here objects (deref refs/workspace-page-objects) - color (if (and (= total 1) ^boolean (ctn/in-any-component? objects shape)) + color (if (and (= total 1) ^boolean + (or (ctn/in-any-component? objects shape) + (ctk/is-variant-container? shape))) selection-rect-color-component selection-rect-color-normal)] diff --git a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs index dcc3f1eef..bd8c9383e 100644 --- a/frontend/src/app/main/ui/workspace/viewport/widgets.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/widgets.cljs @@ -92,7 +92,7 @@ objects (deref refs/workspace-page-objects) color (if selected? - (if (ctn/in-any-component? objects frame) + (if (or (ctn/in-any-component? objects frame) (ctk/is-variant-container? frame)) "var(--assets-component-hightlight)" "var(--color-accent-tertiary)") "#8f9da3") ;; TODO: Set this color on the DS diff --git a/frontend/src/app/plugins/library.cljs b/frontend/src/app/plugins/library.cljs index a269d2ab5..87d140fca 100644 --- a/frontend/src/app/plugins/library.cljs +++ b/frontend/src/app/plugins/library.cljs @@ -19,6 +19,7 @@ [app.main.data.workspace :as dw] [app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.texts :as dwt] + [app.main.data.workspace.variants :as dwv] [app.main.repo :as rp] [app.main.store :as st] [app.plugins.format :as format] @@ -641,7 +642,7 @@ :else (let [component (u/proxy->library-component self) value (dm/str (d/nilv (:path component) "") " / " value)] - (st/emit! (dwl/rename-component-and-main-instance id value)))))} + (st/emit! (dwv/rename-comp-or-variant-and-main id value)))))} :path {:this true diff --git a/frontend/translations/en.po b/frontend/translations/en.po index 3c3f17717..448a67bd6 100644 --- a/frontend/translations/en.po +++ b/frontend/translations/en.po @@ -1651,6 +1651,9 @@ msgstr "Upper Case" msgid "inspect.attributes.variant" msgstr "Variant properties" +msgid "inspect.attributes.variants" +msgstr "Variants properties" + #: src/app/main/ui/inspect/right_sidebar.cljs:163 msgid "inspect.empty.help" msgstr "If you want to know more about design inspect visit Penpot's help center" @@ -4380,6 +4383,9 @@ msgstr "Components" msgid "workspace.assets.components.add-component" msgstr "Add component" +msgid "workspace.assets.components.num-variants" +msgstr "%s Variants" + #: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127 msgid "workspace.assets.create-group" msgstr "Create a group" diff --git a/frontend/translations/es.po b/frontend/translations/es.po index 852e9ee70..6a9624a45 100644 --- a/frontend/translations/es.po +++ b/frontend/translations/es.po @@ -1672,6 +1672,9 @@ msgstr "Mayúsculas" msgid "inspect.attributes.variant" msgstr "Propiedades de la variante" +msgid "inspect.attributes.variants" +msgstr "Propiedades de las variantes" + #: src/app/main/ui/inspect/right_sidebar.cljs:163 msgid "inspect.empty.help" msgstr "" @@ -4407,6 +4410,9 @@ msgstr "Componentes" msgid "workspace.assets.components.add-component" msgstr "Añadir componente" +msgid "workspace.assets.components.num-variants" +msgstr "%s Variantes" + #: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127 msgid "workspace.assets.create-group" msgstr "Crear un grupo"