🐛 Fix bugs from varaints design review

This commit is contained in:
Pablo Alba 2025-03-28 11:20:10 +01:00 committed by GitHub
parent bd5e47f5fc
commit b6c4376217
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 286 additions and 86 deletions

View file

@ -607,6 +607,16 @@
(log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error)) (log/error :hint "Variant error code, we don't want to auto repair it for now" :code (:code error))
file) 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 (defmethod repair-error :default
[_ error file _] [_ error file _]
(log/error :hint "Unknown error code, don't know how to repair" :code (:code error)) (log/error :hint "Unknown error code, don't know how to repair" :code (:code error))

View file

@ -66,6 +66,8 @@
:variant-not-main :variant-not-main
:parent-not-variant :parent-not-variant
:variant-bad-name :variant-bad-name
:variant-bad-variant-name
:variant-component-bad-name
:variant-no-properties}) :variant-no-properties})
(def ^:private schema:error (def ^:private schema:error
@ -414,7 +416,8 @@
(defn- check-variant-container (defn- check-variant-container
"Shape is a variant container, so: "Shape is a variant container, so:
-all its children should be variants with variant-id equals to the shape-id -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] [shape file page]
(let [shape-id (:id shape) (let [shape-id (:id shape)
shapes (:shapes shape) shapes (:shapes shape)
@ -439,7 +442,9 @@
"Shape is a variant, so "Shape is a variant, so
-it should be a main component -it should be a main component
-its parent should be a variant-container -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] [shape file page]
(let [parent (ctst/get-shape page (:parent-id shape)) (let [parent (ctst/get-shape page (:parent-id shape))
component (ctkl/get-component (:data file) (:component-id shape) true) component (ctkl/get-component (:data file) (:component-id shape) true)
@ -454,8 +459,16 @@
shape file page)) shape file page))
(when-not (= name (:variant-name shape)) (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)) (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)))) shape file page))))
(defn- check-shape (defn- check-shape
@ -565,7 +578,9 @@
-It should have at least one variant property" -It should have at least one variant property"
[component file] [component file]
(let [component-page (ctf/get-component-page (:data file) component) (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) (when-not (ctk/is-variant? main-component)
(report-error :not-a-variant (report-error :not-a-variant
(str/ffmt "Shape % should be a variant" (:id main-component)) (str/ffmt "Shape % should be a variant" (:id main-component))

View file

@ -6,6 +6,7 @@
(ns app.common.files.variant (ns app.common.files.variant
(:require (:require
[app.common.data.macros :as dm] [app.common.data.macros :as dm]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctcl] [app.common.types.components-list :as ctcl]
[app.common.types.variant :as ctv] [app.common.types.variant :as ctv]
[cuerdas.core :as str])) [cuerdas.core :as str]))
@ -61,3 +62,22 @@
(and (seq shapes) (and (seq shapes)
(not= (:main-instance-id component) (last 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)))

View file

@ -256,7 +256,7 @@
child-heads-ids (map :id child-heads) 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 component-main-parent
(ctn/find-component-main objects parent false) (ctn/find-component-main objects parent false)
@ -384,7 +384,7 @@
#(-> (dissoc % :variant-id :variant-name) #(-> (dissoc % :variant-id :variant-name)
(assoc :name new-name)))))) (assoc :name new-name))))))
changes changes
variant-heads)))) variant-shapes))))
;; Add variant info and rename when moving into a different variant-container ;; Add variant info and rename when moving into a different variant-container
(cond-> (ctk/is-variant-container? parent) (cond-> (ctk/is-variant-container? parent)

View file

@ -51,7 +51,7 @@
(def property-prefix "Property") (def property-prefix "Property")
(def property-regex (re-pattern (str property-prefix "(\\d+)"))) (def property-regex (re-pattern (str property-prefix "(\\d+)")))
(def value-prefix "Value") (def value-prefix "Value ")
(defn properties-to-name (defn properties-to-name

View file

@ -117,7 +117,7 @@
(t/is (= (count (:variant-properties comp01')) 2)) (t/is (= (count (:variant-properties comp01')) 2))
(t/is (= (count (:variant-properties comp02)) 1)) (t/is (= (count (:variant-properties comp02)) 1))
(t/is (= (count (:variant-properties comp02')) 2)) (t/is (= (count (:variant-properties comp02')) 2))
(t/is (= (-> comp01' :variant-properties last :value) "Value1")))) (t/is (= (-> comp01' :variant-properties last :value) "Value 1"))))

View file

@ -13,6 +13,7 @@
[app.common.features :as cfeat] [app.common.features :as cfeat]
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.geom.align :as gal] [app.common.geom.align :as gal]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.proportions :as gpp] [app.common.geom.proportions :as gpp]
@ -25,7 +26,7 @@
[app.common.schema :as sm] [app.common.schema :as sm]
[app.common.text :as txt] [app.common.text :as txt]
[app.common.transit :as t] [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.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf] [app.common.types.file :as ctf]
@ -75,6 +76,7 @@
[app.main.data.workspace.thumbnails :as dwth] [app.main.data.workspace.thumbnails :as dwth]
[app.main.data.workspace.transforms :as dwt] [app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu] [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.viewport :as dwv]
[app.main.data.workspace.zoom :as dwz] [app.main.data.workspace.zoom :as dwz]
[app.main.errors] [app.main.errors]
@ -577,7 +579,7 @@
name (cfh/generate-unique-name base-name unames :suffix-fn suffix-fn) name (cfh/generate-unique-name base-name unames :suffix-fn suffix-fn)
objects (update-vals (:objects page) #(dissoc % :use-for-thumbnail)) 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))) ids-to-remove (set (apply concat (map #(cfh/get-children-ids objects %) main-instances-ids)))
add-component-copy add-component-copy
@ -794,6 +796,10 @@
([] (end-rename-shape nil nil)) ([] (end-rename-shape nil nil))
([shape-id name] ([shape-id name]
(ptk/reify ::end-rename-shape (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 ptk/WatchEvent
(watch [_ state _] (watch [_ state _]
(when-let [shape-id (d/nilv shape-id (dm/get-in state [:workspace-local :shape-for-rename]))] (when-let [shape-id (d/nilv shape-id (dm/get-in state [:workspace-local :shape-for-rename]))]
@ -802,19 +808,24 @@
clean-name (cfh/clean-path name) clean-name (cfh/clean-path name)
valid? (and (not (str/ends-with? name "/")) valid? (and (not (str/ends-with? name "/"))
(string? clean-name) (string? clean-name)
(not (str/blank? clean-name)))] (not (str/blank? clean-name)))
(rx/concat component-id (:component-id shape)
;; Remove rename state from workspace local state undo-id (js/Symbol)]
(rx/of #(update % :workspace-local dissoc :shape-for-rename))
;; Rename the shape if string is not empty/blank
(when valid? (when valid?
(rx/of (update-shape shape-id {:name clean-name}))) (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 if shape is a main instance ;; Update the component in case shape is a main instance
(when (and valid? (:main-instance shape)) (when (and (some? component-id) (ctc/main-instance? shape))
(when-let [component-id (:component-id shape)] (dwl/rename-component component-id clean-name))
(rx/of (dwl/rename-component component-id clean-name))))))))))) (dwu/commit-undo-transaction undo-id))))))))))
;; --- Update Selected Shapes attrs ;; --- Update Selected Shapes attrs
@ -1208,7 +1219,8 @@
(watch [_ state _] (watch [_ state _]
(let [file-id (:current-file-id state) (let [file-id (:current-file-id state)
fdata (dsh/lookup-file-data state file-id) fdata (dsh/lookup-file-data state file-id)
cpath (dm/get-in fdata [:components component-id :path]) component (cfv/get-primary-component fdata component-id)
cpath (:path component)
cpath (cfh/split-path cpath) cpath (cfh/split-path cpath)
paths (map (fn [i] (cfh/join-path (take (inc i) cpath))) paths (map (fn [i] (cfh/join-path (take (inc i) cpath)))
(range (count cpath)))] (range (count cpath)))]
@ -1217,11 +1229,14 @@
(rx/of (dcm/go-to-workspace :layout :assets) (rx/of (dcm/go-to-workspace :layout :assets)
(set-assets-section-open file-id :library true) (set-assets-section-open file-id :library true)
(set-assets-section-open file-id :components 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 ptk/EffectEvent
(effect [_ _ _] (effect [_ state _]
(let [wrapper-id (str "component-shape-id-" component-id)] (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))))))) (tm/schedule-on-idle #(dom/scroll-into-view-if-needed! (dom/get-element wrapper-id)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@ -1388,7 +1403,7 @@
heads)))) heads))))
(advance-copy [file libraries page objects shape] (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)] (let [level-delta (ctn/get-nesting-level-delta (:objects page) shape uuid/zero)]
(if (pos? level-delta) (if (pos? level-delta)
(reduce (partial advance-shape file libraries page level-delta) (reduce (partial advance-shape file libraries page level-delta)
@ -2118,7 +2133,7 @@
undo-id (js/Symbol)] undo-id (js/Symbol)]
(rx/concat (rx/concat
(->> (filter ctk/instance-head? orig-shapes) (->> (filter ctc/instance-head? orig-shapes)
(map (fn [{:keys [component-file]}] (map (fn [{:keys [component-file]}]
(ptk/event ::ev/event (ptk/event ::ev/event
{::ev/name "use-library-component" {::ev/name "use-library-component"
@ -2433,7 +2448,7 @@
(let [objects (dsh/lookup-page-objects state) (let [objects (dsh/lookup-page-objects state)
copies (->> objects copies (->> objects
vals 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) copies-no-ref (filter #(not (:shape-ref %)) copies)
find-childs-no-ref (fn [acc-map item] find-childs-no-ref (fn [acc-map item]

View file

@ -242,12 +242,13 @@
selected-shapes (map (d/getf objects) selected) selected-shapes (map (d/getf objects) selected)
single? (= (count selected-shapes) 1) single? (= (count selected-shapes) 1)
is-frame? (= :frame (:type (first selected-shapes))) 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)] undo-id (js/Symbol)]
(rx/of (rx/of
(dwu/start-undo-transaction undo-id) (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-id (first selected) type :from-frame? true)
(create-layout-from-selection type)) (create-layout-from-selection type))
(dwu/commit-undo-transaction undo-id)))))) (dwu/commit-undo-transaction undo-id))))))

View file

@ -10,10 +10,12 @@
[app.common.data :as d] [app.common.data :as d]
[app.common.files.changes-builder :as pcb] [app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.logic.variant-properties :as clvp] [app.common.logic.variant-properties :as clvp]
[app.common.logic.variants :as clv] [app.common.logic.variants :as clv]
[app.common.types.component :as ctc] [app.common.types.component :as ctc]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.types.shape.layout :as ctsl]
[app.common.uuid :as uuid] [app.common.uuid :as uuid]
[app.main.data.changes :as dch] [app.main.data.changes :as dch]
[app.main.data.helpers :as dsh] [app.main.data.helpers :as dsh]
@ -22,6 +24,7 @@
[app.main.data.workspace.selection :as dws] [app.main.data.workspace.selection :as dws]
[app.main.data.workspace.shape-layout :as dwsl] [app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh] [app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.features :as features] [app.main.features :as features]
[app.util.dom :as dom] [app.util.dom :as dom]
@ -144,6 +147,25 @@
(dom/focus! (dom/get-element (str "variant-prop-" shape-id prop-num)))))) (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 (defn add-new-variant
"Create a new variant and add it to the variant-container" "Create a new variant and add it to the variant-container"
[shape-id] [shape-id]
@ -161,6 +183,10 @@
component-id (:component-id shape) component-id (:component-id shape)
component (ctkl/get-component data component-id) 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-component-id (uuid/next)
new-shape-id (uuid/next) new-shape-id (uuid/next)
@ -177,6 +203,8 @@
(rx/of (rx/of
(dwu/start-undo-transaction undo-id) (dwu/start-undo-transaction undo-id)
(dch/commit-changes changes) (dch/commit-changes changes)
(when-not has-layout?
(resposition-and-resize-variant new-shape-id))
(dwu/commit-undo-transaction undo-id) (dwu/commit-undo-transaction undo-id)
(ptk/data-event :layout/update {:ids [(:parent-id shape)]}) (ptk/data-event :layout/update {:ids [(:parent-id shape)]})
(dws/select-shape new-shape-id)) (dws/select-shape new-shape-id))
@ -196,7 +224,7 @@
page-id (:current-page-id state) page-id (:current-page-id state)
objects (dsh/lookup-page-objects state file-id page-id) objects (dsh/lookup-page-objects state file-id page-id)
main (get objects main-instance-id) main (get objects main-instance-id)
main-id (:id main) parent (get objects (:parent-id main))
component-id (:component-id main) component-id (:component-id main)
cpath (cfh/split-path (:name main)) cpath (cfh/split-path (:name main))
name (first cpath) name (first cpath)
@ -237,9 +265,16 @@
(cl/remove-all-fills variant-vec {:color clr/black :opacity 1}) (cl/remove-all-fills variant-vec {:color clr/black :opacity 1})
(dwsl/create-layout-from-id variant-id :flex) (dwsl/create-layout-from-id variant-id :flex)
(dwsh/update-shapes variant-vec #(merge % cont-props)) (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) (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 ;; Add the necessary number of new properties, with default values
(rx/from (rx/from
@ -310,3 +345,40 @@
(rx/from (map add-new-variant selected-ids)) (rx/from (map add-new-variant selected-ids))
(rx/of (dwu/commit-undo-transaction undo-id))) (rx/of (dwu/commit-undo-transaction undo-id)))
(rx/of (dws/duplicate-selected true))))))) (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)))))))

View file

@ -30,9 +30,9 @@
(mf/defc variant-panel* (mf/defc variant-panel*
[{:keys [objects shapes libraries file-id] :as kk}] [{:keys [objects shapes libraries file-id] :as kk}]
(let [shape (->> shapes first) (let [shape (->> shapes first)
is-container? (ctc/is-variant-container? shape)
properties (mf/with-memo [objects shape] properties (mf/with-memo [objects shape]
(let [data (dm/get-in libraries [file-id :data]) (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)))] component (when-not is-container? (ctkl/get-component data (:component-id shape)))]
(if is-container? (if is-container?
(->> (cfv/extract-properties-values data objects (:id shape)) (->> (cfv/extract-properties-values data objects (:id shape))
@ -41,7 +41,7 @@
(map #(update % :value (fn [v] (if (str/blank? v) "--" v))))))))] (map #(update % :value (fn [v] (if (str/blank? v) "--" v))))))))]
[:div {:class (stl/css :attributes-block)} [:div {:class (stl/css :attributes-block)}
[:> inspect-title-bar* [:> 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)}] :class (stl/css :title-spacing-variant)}]
(for [[pos property] (map-indexed vector properties)] (for [[pos property] (map-indexed vector properties)]

View file

@ -19,6 +19,7 @@
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.media :as dwm] [app.main.data.workspace.media :as dwm]
[app.main.data.workspace.undo :as dwu] [app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.variants :as dwv]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.store :as st] [app.main.store :as st]
[app.main.ui.components.editable-label :refer [editable-label]] [app.main.ui.components.editable-label :refer [editable-label]]
@ -54,7 +55,7 @@
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [component renaming listing-thumbs? selected [{:keys [component renaming listing-thumbs? selected
file-id on-asset-click on-context-menu on-drag-start do-rename 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) (let [item-ref (mf/use-ref)
@ -95,7 +96,7 @@
(fn [event] (fn [event]
(when (and is-local (:is-local @drag-data*)) (when (and is-local (:is-local @drag-data*))
(cmm/on-drop-asset event component dragging* selected selected-full (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 on-drag-enter
(mf/use-fn (mf/use-fn
@ -129,7 +130,8 @@
[:div {:ref item-ref [:div {:ref item-ref
:class (stl/css-case :selected (contains? selected (:id component)) :class (stl/css-case :selected (contains? selected (:id component))
:grid-cell listing-thumbs? :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)) :id (dm/str "component-shape-id-" (:id component))
:draggable (and (not read-only?) (not renaming?)) :draggable (and (not read-only?) (not renaming?))
:on-click on-component-click :on-click on-component-click
@ -166,13 +168,16 @@
:root-shape root-shape :root-shape root-shape
:component component :component component
:container container :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/defc components-group
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [file-id prefix groups open-groups force-open? renaming listing-thumbs? selected on-asset-click [{: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 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 (let [group-open? (if (false? (get open-groups prefix)) ;; if the user has closed it specifically, respect that
false false
@ -205,7 +210,7 @@
(mf/deps dragging* prefix selected-paths selected-full is-local drag-data*) (mf/deps dragging* prefix selected-paths selected-full is-local drag-data*)
(fn [event] (fn [event]
(when (and is-local (:is-local @drag-data*)) (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) [:div {:class (stl/css :component-group)
:on-drag-enter on-drag-enter :on-drag-enter on-drag-enter
@ -220,7 +225,6 @@
:on-rename on-rename-group :on-rename on-rename-group
:on-ungroup on-ungroup}] :on-ungroup on-ungroup}]
(when group-open? (when group-open?
[:* [:*
(let [components (not-empty (get groups "" []))] (let [components (not-empty (get groups "" []))]
@ -257,7 +261,8 @@
:on-group on-group :on-group on-group
:do-rename do-rename :do-rename do-rename
:cancel-rename cancel-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] (for [[path-item content] groups]
(when-not (empty? path-item) (when-not (empty? path-item)
@ -278,13 +283,14 @@
:on-ungroup on-ungroup :on-ungroup on-ungroup
:on-context-menu on-context-menu :on-context-menu on-context-menu
:selected-full selected-full :selected-full selected-full
:is-local is-local}]))])])) :is-local is-local
:count-variants count-variants}]))])]))
(mf/defc components-section (mf/defc components-section
{::mf/wrap-props false} {::mf/wrap-props false}
[{:keys [file-id is-local components listing-thumbs? open? force-open? [{:keys [file-id is-local components listing-thumbs? open? force-open?
reverse-sort? selected on-asset-click on-assets-delete 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) (let [input-ref (mf/use-ref nil)
@ -379,7 +385,7 @@
(swap! state* dissoc :renaming) (swap! state* dissoc :renaming)
(when (not (str/blank? new-name)) (when (not (str/blank? new-name))
(st/emit! (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 on-context-menu
(mf/use-fn (mf/use-fn
@ -408,7 +414,7 @@
(filter #(if multi-components? (filter #(if multi-components?
(contains? selected (:id %)) (contains? selected (:id %))
(= current-component-id (:id %)))) (= current-component-id (:id %))))
(map #(dwl/rename-component-and-main-instance (map #(dwv/rename-comp-or-variant-and-main
(:id %) (:id %)
(cmm/add-group % group-name))))) (cmm/add-group % group-name)))))
(st/emit! (dwu/commit-undo-transaction undo-id))))) (st/emit! (dwu/commit-undo-transaction undo-id)))))
@ -423,7 +429,7 @@
(run! st/emit! (run! st/emit!
(->> components (->> components
(filter #(str/starts-with? (:path %) path)) (filter #(str/starts-with? (:path %) path))
(map #(dwl/rename-component-and-main-instance (map #(dwv/rename-comp-or-variant-and-main
(:id %) (:id %)
(cmm/rename-group % path last-path))))) (cmm/rename-group % path last-path)))))
(st/emit! (dwu/commit-undo-transaction undo-id))))) (st/emit! (dwu/commit-undo-transaction undo-id)))))
@ -454,7 +460,7 @@
(run! st/emit! (run! st/emit!
(->> components (->> components
(filter #(str/starts-with? (:path %) path)) (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))))) (st/emit! (dwu/commit-undo-transaction undo-id)))))
on-drag-start on-drag-start
@ -544,7 +550,8 @@
:on-ungroup on-ungroup :on-ungroup on-ungroup
:on-context-menu on-context-menu :on-context-menu on-context-menu
:selected-full selected-full :selected-full selected-full
:is-local ^boolean is-local}]) :is-local ^boolean is-local
:count-variants count-variants}])
[:& cmm/assets-context-menu [:& cmm/assets-context-menu
{:on-close on-close-menu {:on-close on-close-menu
@ -564,7 +571,7 @@
{:name (tr "workspace.assets.delete") {:name (tr "workspace.assets.delete")
:id "assets-delete-component" :id "assets-delete-component"
:handler on-delete}) :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") {:name (tr "workspace.assets.group")
:id "assets-group-component" :id "assets-group-component"
:handler on-group}) :handler on-group})

View file

@ -157,6 +157,10 @@
} }
} }
.enum-item-with-mark {
grid-template-columns: auto 1fr $s-24;
}
.item-name { .item-name {
@include bodySmallTypography; @include bodySmallTypography;
@include textEllipsis; @include textEllipsis;
@ -219,3 +223,26 @@
background-color: var(--assets-item-background-color-drag); background-color: var(--assets-item-background-color-drag);
border: $s-2 solid var(--assets-item-border-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);
}
}

View file

@ -145,7 +145,7 @@
(mf/defc file-library-content* (mf/defc file-library-content*
{::mf/private true} {::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) (let [open-status (mf/deref open-status-ref)
file-id (:id file) file-id (:id file)
@ -263,7 +263,8 @@
:selected selected :selected selected
:on-asset-click on-component-click :on-asset-click on-component-click
:on-assets-delete on-assets-delete :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? (when ^boolean show-graphics?
[:& graphics-section [:& graphics-section
@ -385,7 +386,15 @@
(mf/use-fn (mf/use-fn
(mf/deps file-id) (mf/deps file-id)
(fn [] (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) [:div {:class (stl/css :tool-window)
:on-context-menu dom/prevent-default :on-context-menu dom/prevent-default
@ -409,4 +418,5 @@
:media filtered-media :media filtered-media
:typographies filtered-typographies :typographies filtered-typographies
:on-clear-selection unselect-all :on-clear-selection unselect-all
:open-status-ref open-status-ref}])])) :open-status-ref open-status-ref
:count-variants count-variants}])]))

View file

@ -57,7 +57,8 @@
main-instance? (or (not components-v2) (:main-instance item)) main-instance? (or (not components-v2) (:main-instance item))
variants? (features/use-feature "variants/v1") variants? (features/use-feature "variants/v1")
is-variant? (when variants? (ctk/is-variant? item)) is-variant? (when variants? (ctk/is-variant? item))
variant-name (when is-variant? (:variant-name item))] variant-name (when is-variant? (:variant-name item))
is-variant-container? (when variants? (ctk/is-variant-container? item))]
[:* [:*
[:div {:id id [:div {:id id
:ref dref :ref dref
@ -72,7 +73,7 @@
:selected selected? :selected selected?
:type-frame (cfh/frame-shape? item) :type-frame (cfh/frame-shape? item)
:type-bool (cfh/bool-shape? item) :type-bool (cfh/bool-shape? item)
:type-comp component-tree? :type-comp (or component-tree? is-variant-container?)
:hidden hidden? :hidden hidden?
:dnd-over dnd-over? :dnd-over dnd-over?
:dnd-over-top dnd-over-top? :dnd-over-top dnd-over-top?
@ -132,7 +133,7 @@
:is-blocked blocked? :is-blocked blocked?
:parent-size parent-size :parent-size parent-size
:is-selected selected? :is-selected selected?
:type-comp component-tree? :type-comp (or component-tree? is-variant-container?)
:type-frame (cfh/frame-shape? item) :type-frame (cfh/frame-shape? item)
:variant-name variant-name :variant-name variant-name
:is-hidden hidden?}] :is-hidden hidden?}]

View file

@ -714,6 +714,7 @@
width: 100%; width: 100%;
display: flex; display: flex;
gap: var(--sp-xs); gap: var(--sp-xs);
padding-right: var(--sp-xxs);
} }
.variant-property-name-bg { .variant-property-name-bg {

View file

@ -11,6 +11,7 @@
[app.common.exceptions :as ex] [app.common.exceptions :as ex]
[app.common.files.helpers :as cfh] [app.common.files.helpers :as cfh]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.main.refs :as refs] [app.main.refs :as refs]
[app.main.ui.hooks :as hooks] [app.main.ui.hooks :as hooks]
@ -32,7 +33,9 @@
;; NOTE: that we don't use mf/deref to avoid a repaint dependency here ;; NOTE: that we don't use mf/deref to avoid a repaint dependency here
objects (deref refs/workspace-page-objects) 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(--assets-component-hightlight)"
"var(--color-accent-tertiary)") "var(--color-accent-tertiary)")

View file

@ -12,6 +12,7 @@
[app.common.geom.matrix :as gmt] [app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt] [app.common.geom.point :as gpt]
[app.common.geom.shapes :as gsh] [app.common.geom.shapes :as gsh]
[app.common.types.component :as ctk]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
@ -530,7 +531,9 @@
;; Note that we don't use mf/deref to avoid a repaint dependency here ;; Note that we don't use mf/deref to avoid a repaint dependency here
objects (deref refs/workspace-page-objects) 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-component
selection-rect-color-normal)] selection-rect-color-normal)]
@ -577,7 +580,9 @@
;; Note that we don't use mf/deref to avoid a repaint dependency here ;; Note that we don't use mf/deref to avoid a repaint dependency here
objects (deref refs/workspace-page-objects) 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-component
selection-rect-color-normal)] selection-rect-color-normal)]

View file

@ -92,7 +92,7 @@
objects (deref refs/workspace-page-objects) objects (deref refs/workspace-page-objects)
color (if selected? 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(--assets-component-hightlight)"
"var(--color-accent-tertiary)") "var(--color-accent-tertiary)")
"#8f9da3") ;; TODO: Set this color on the DS "#8f9da3") ;; TODO: Set this color on the DS

View file

@ -19,6 +19,7 @@
[app.main.data.workspace :as dw] [app.main.data.workspace :as dw]
[app.main.data.workspace.libraries :as dwl] [app.main.data.workspace.libraries :as dwl]
[app.main.data.workspace.texts :as dwt] [app.main.data.workspace.texts :as dwt]
[app.main.data.workspace.variants :as dwv]
[app.main.repo :as rp] [app.main.repo :as rp]
[app.main.store :as st] [app.main.store :as st]
[app.plugins.format :as format] [app.plugins.format :as format]
@ -641,7 +642,7 @@
:else :else
(let [component (u/proxy->library-component self) (let [component (u/proxy->library-component self)
value (dm/str (d/nilv (:path component) "") " / " value)] 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 :path
{:this true {:this true

View file

@ -1651,6 +1651,9 @@ msgstr "Upper Case"
msgid "inspect.attributes.variant" msgid "inspect.attributes.variant"
msgstr "Variant properties" msgstr "Variant properties"
msgid "inspect.attributes.variants"
msgstr "Variants properties"
#: src/app/main/ui/inspect/right_sidebar.cljs:163 #: src/app/main/ui/inspect/right_sidebar.cljs:163
msgid "inspect.empty.help" msgid "inspect.empty.help"
msgstr "If you want to know more about design inspect visit Penpot's help center" 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" msgid "workspace.assets.components.add-component"
msgstr "Add component" msgstr "Add component"
msgid "workspace.assets.components.num-variants"
msgstr "%s Variants"
#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127 #: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127
msgid "workspace.assets.create-group" msgid "workspace.assets.create-group"
msgstr "Create a group" msgstr "Create a group"

View file

@ -1672,6 +1672,9 @@ msgstr "Mayúsculas"
msgid "inspect.attributes.variant" msgid "inspect.attributes.variant"
msgstr "Propiedades de la variante" msgstr "Propiedades de la variante"
msgid "inspect.attributes.variants"
msgstr "Propiedades de las variantes"
#: src/app/main/ui/inspect/right_sidebar.cljs:163 #: src/app/main/ui/inspect/right_sidebar.cljs:163
msgid "inspect.empty.help" msgid "inspect.empty.help"
msgstr "" msgstr ""
@ -4407,6 +4410,9 @@ msgstr "Componentes"
msgid "workspace.assets.components.add-component" msgid "workspace.assets.components.add-component"
msgstr "Añadir componente" msgstr "Añadir componente"
msgid "workspace.assets.components.num-variants"
msgstr "%s Variantes"
#: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127 #: src/app/main/ui/workspace/sidebar/assets/groups.cljs:127
msgid "workspace.assets.create-group" msgid "workspace.assets.create-group"
msgstr "Crear un grupo" msgstr "Crear un grupo"