🎉 Create a new variant from an existing one

This commit is contained in:
Pablo Alba 2025-03-04 13:52:40 +01:00 committed by GitHub
parent aa468e2153
commit 8eb2aaa0a8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 207 additions and 56 deletions

View file

@ -26,6 +26,7 @@
[app.main.data.workspace.texts :as dwtxt]
[app.main.data.workspace.transforms :as dwt]
[app.main.data.workspace.undo :as dwu]
[app.main.data.workspace.variants :as dwv]
[app.main.features :as features]
[app.main.refs :as refs]
[app.main.store :as st]
@ -121,7 +122,7 @@
:duplicate {:tooltip (ds/meta "D")
:command (ds/c-mod "d")
:subsections [:edit]
:fn #(emit-when-no-readonly (dw/duplicate-selected true))}
:fn #(emit-when-no-readonly (dwv/duplicate-or-add-variant))}
:start-editing {:tooltip (ds/enter)
:command "enter"
@ -175,7 +176,7 @@
:create-component {:tooltip (ds/meta "K")
:command (ds/c-mod "k")
:subsections [:modify-layers]
:fn #(emit-when-no-readonly (dwl/add-component))}
:fn #(emit-when-no-readonly (dwv/add-component-or-variant))}
:detach-component {:tooltip (ds/meta-shift "K")
:command (ds/c-mod "shift+k")

View file

@ -7,17 +7,24 @@
(ns app.main.data.workspace.variants
(:require
[app.common.colors :as clr]
[app.common.data :as d]
[app.common.data.macros :as dm]
[app.common.files.changes-builder :as pcb]
[app.common.logic.libraries :as cll]
[app.common.logic.variants :as clv]
[app.common.types.component :as ctc]
[app.common.types.components-list :as ctkl]
[app.common.uuid :as uuid]
[app.main.data.changes :as dch]
[app.main.data.helpers :as dsh]
[app.main.data.workspace.colors :as cl]
[app.main.data.workspace.libraries :as dwl]
[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.undo :as dwu]
[app.main.features :as features]
[app.util.dom :as dom]
[beicon.v2.core :as rx]
[potok.v2.core :as ptk]))
@ -131,6 +138,70 @@
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id))))))
(defn focus-property
[shape-id prop-num]
(ptk/reify ::focus-property
ptk/EffectEvent
(effect [_ _ _]
(dom/focus! (dom/get-element (str "variant-prop-" shape-id prop-num))))))
(defn add-new-variant
"Create a new variant and add it to the variant-container"
[shape-id]
(ptk/reify ::add-new-variant
ptk/WatchEvent
(watch [it state _]
(let [page-id (:current-page-id state)
data (dsh/lookup-file-data state)
objects (-> (dsh/get-page data page-id)
(get :objects))
shape (get objects shape-id)
shape (if (ctc/is-variant-container? shape)
(get objects (last (:shapes shape)))
shape)
component-id (:component-id shape)
component (ctkl/get-component data component-id)
new-component-id (uuid/next)
new-shape-id (uuid/next)
value (str clv/value-prefix
(-> (clv/extract-properties-values data objects (:variant-id component))
last
:values
count
inc))
prop-num (dec (count (:variant-properties component)))
[new-shape changes] (-> (pcb/empty-changes it page-id)
(pcb/with-library-data data)
(pcb/with-objects objects)
(pcb/with-page-id page-id)
(cll/generate-duplicate-component
{:data data}
component-id
new-component-id
true
{:new-shape-id new-shape-id :apply-changes-local-library? true}))
changes (-> changes
(clv/generate-update-property-value new-component-id prop-num value)
(pcb/change-parent (:parent-id shape) [new-shape] 0))
undo-id (js/Symbol)]
(rx/concat
(rx/of
(dwu/start-undo-transaction undo-id)
(dch/commit-changes changes)
(dwu/commit-undo-transaction undo-id)
(ptk/data-event :layout/update {:ids [(:parent-id shape)]})
(dws/select-shape new-shape-id))
(->> (rx/of (focus-property new-shape-id prop-num))
(rx/delay 250)))))))
(defn transform-in-variant
"Given the id of a main shape of a component, creates a variant structure for
that component"
@ -174,3 +245,55 @@
(set-variant-id new-component-id variant-id)
(add-new-property variant-id {:fill-values? true})
(dwu/commit-undo-transaction undo-id))))))
(defn add-component-or-variant
[]
(ptk/reify ::add-component-or-variant
ptk/WatchEvent
(watch [_ state _]
(let [variants? (features/active-feature? state "variants/v1")
objects (dsh/lookup-page-objects state)
selected-ids (dsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected-ids)
single? (= 1 (count selected-ids))
first-shape (first selected-shapes)
transform-in-variant? (and variants?
single?
(not (ctc/is-variant? first-shape))
(ctc/main-instance? first-shape))
add-new-variant? (and variants?
(every? ctc/is-variant? selected-shapes))
undo-id (js/Symbol)]
(cond
transform-in-variant?
(rx/of (transform-in-variant (:id first-shape)))
add-new-variant?
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/from (map add-new-variant selected-ids))
(rx/of (dwu/commit-undo-transaction undo-id)))
:else
(rx/of (dwl/add-component)))))))
(defn duplicate-or-add-variant
[]
(ptk/reify ::duplicate-or-add-variant
ptk/WatchEvent
(watch [_ state _]
(let [variants? (features/active-feature? state "variants/v1")
objects (dsh/lookup-page-objects state)
selected-ids (dsh/lookup-selected state)
selected-shapes (map (d/getf objects) selected-ids)
add-new-variant? (and variants?
(every? ctc/is-variant? selected-shapes))
undo-id (js/Symbol)]
(if add-new-variant?
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/from (map add-new-variant selected-ids))
(rx/of (dwu/commit-undo-transaction undo-id)))
(rx/of (dws/duplicate-selected true)))))))

View file

@ -27,6 +27,7 @@
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
[app.main.data.workspace.shortcuts :as sc]
[app.main.data.workspace.variants :as dwv]
[app.main.refs :as refs]
[app.main.store :as st]
[app.main.ui.components.dropdown :refer [dropdown]]
@ -555,8 +556,10 @@
can-make-component (every? true? (map #(ctn/valid-shape-for-component? objects %) shapes))
heads (filter ctk/instance-head? shapes)
components-menu-entries (cmm/generate-components-menu-entries heads true)
variant-container? (and single? (ctk/is-variant-container? (first shapes)))
do-add-component #(st/emit! (dwl/add-component))
do-add-multiple-components #(st/emit! (dwl/add-multiple-components))]
do-add-multiple-components #(st/emit! (dwl/add-multiple-components))
do-add-variant #(st/emit! (dwv/add-new-variant (:id (first shapes))))]
[:*
(when can-make-component ;; We don't want to change the structure of component copies
[:*
@ -577,7 +580,13 @@
:title (:title entry)
:shortcut (when (contains? entry :shortcut)
(sc/get-tooltip (:shortcut entry)))
:on-click (:action entry)}])])]))
:on-click (:action entry)}])])
(when variant-container?
[:> menu-separator*]
[:> menu-entry* {:title (tr "workspace.shape.menu.add-variant")
:shortcut (sc/get-tooltip :create-component)
:on-click do-add-variant}])]))
(mf/defc context-menu-delete*
{::mf/props :obj

View file

@ -471,5 +471,6 @@
:action do-update-component})
(when (and variants? (not multi) main-instance?)
{:title (tr "workspace.shape.menu.add-variant")
:shortcut :create-component
:action do-add-variant})]]
(filter (complement nil?) menu-entries)))

View file

@ -288,11 +288,12 @@
[:*
(for [[pos prop] (map vector (range) properties)]
[:div {:key (str (:id shape) (:name prop)) :class (stl/css :variant-property-container)}
[:div {:key (str (:id shape) pos) :class (stl/css :variant-property-container)}
(if (ctk/main-instance? shape)
[:*
[:span {:class (stl/css :variant-property-name :variant-property-name-bg)} (:name prop)]
[:> combobox* {:default-selected (if (str/empty? (:value prop)) "--" (:value prop))
[:> combobox* {:id (str "variant-prop-" (:id shape) pos)
:default-selected (if (str/empty? (:value prop)) "--" (:value prop))
:options (clj->js (get-options (:name prop)))
:on-change (partial change-property-value pos)}]]
@ -741,7 +742,7 @@
(mf/defc variant-menu*
[{:keys [shapes]}]
(let [multi (> (count shapes) 1)
(let [multi? (> (count shapes) 1)
shape (first shapes)
shape-name (:name shape)
@ -757,6 +758,11 @@
first-variant (get objects (first (:shapes shape)))
variant-id (:variant-id first-variant)
dashes-to-end (mf/use-fn
(fn [data]
(let [dashes (if (some #(= % "--") data) ["--"] [])]
(concat (remove #(= % "--") data) dashes))))
properties (mf/with-memo [data objects variant-id]
(->> (dwv/find-related-components data objects variant-id)
(mapcat :variant-properties)
@ -764,17 +770,20 @@
(map-indexed (fn [index [k v]]
{:name k
:pos index
:values (distinct
(map #(if (str/empty? (:value %)) "--" (:value %)) v))}))))
:values (->> v
(map #(if (str/empty? (:value %)) "--" (:value %)))
distinct
dashes-to-end)}))))
menu-open* (mf/use-state false)
menu-open? (deref menu-open*)
menu-entries [{:title (tr "workspace.shape.menu.add-variant-property")
:action #(st/emit! (dwv/add-new-property variant-id))}]
show-menu? (seq menu-entries)
:action #(st/emit! (dwv/add-new-property variant-id))}
{:title (tr "workspace.shape.menu.add-variant")
:action #(st/emit! (dwv/add-new-variant (:id shape)))}]
on-menu-click
(mf/use-fn
@ -821,8 +830,8 @@
[:div {:class (stl/css :element-content)}
[:div {:class (stl/css-case :component-wrapper true
:with-actions show-menu?
:without-actions (not show-menu?))}
:with-actions (not multi?)
:without-actions multi?)}
[:button {:class (stl/css-case :component-name-wrapper true
:with-main true
:swappeable false)}
@ -832,12 +841,12 @@
[:div {:class (stl/css :name-wrapper)}
[:div {:class (stl/css :component-name)}
[:span {:class (stl/css :component-name-inside)}
(if multi
(if multi?
(tr "settings.multiple")
(cfh/last-path shape-name))]]]]
(when show-menu?
(when-not multi?
[:div {:class (stl/css :component-actions)}
[:button {:class (stl/css-case :menu-btn true
:selected menu-open?)
@ -848,11 +857,11 @@
:on-close on-menu-close
:menu-entries menu-entries
:main-instance true}]])]
(when-not multi
(when-not multi?
[:*
(for [property properties]
(for [[pos property] (map vector (range) properties)]
(let [val (str/join ", " (:values property))]
[:div {:key (str (:id shape) (:name property)) :class (stl/css :variant-property-row)}
[:div {:key (str (:id shape) pos) :class (stl/css :variant-property-row)}
[:> input-with-values* {:name (:name property)
:values val
:data-position (:pos property)