Manage overrides on variants switch

This commit is contained in:
Pablo Alba 2025-05-07 09:29:41 +02:00 committed by GitHub
parent ea095a98ba
commit 751bed4117
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 153 additions and 14 deletions

View file

@ -25,6 +25,7 @@
[app.common.types.file :as ctf]
[app.common.types.page :as ctp]
[app.common.types.pages-list :as ctpl]
[app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst]
[app.common.types.shape.interactions :as ctsi]
[app.common.types.shape.layout :as ctl]
@ -43,6 +44,12 @@
(def log-shape-ids #{})
(def log-container-ids #{})
(def updatable-attrs (->> (seq (keys ctk/sync-attrs))
;; We don't update the flex-child attrs
(remove ctk/swap-keep-attrs)
;; We don't do automatic update of the `layout-grid-cells` property.
(remove #(= :layout-grid-cells %))))
(defn enabled-shape?
[id container]
(or (empty? log-shape-ids)
@ -1704,11 +1711,7 @@
origin-shape (reposition-shape origin-shape origin-root dest-root)
touched (get dest-shape :touched #{})]
(loop [attrs (->> (seq (keys ctk/sync-attrs))
;; We don't update the flex-child attrs
(remove ctk/swap-keep-attrs)
;; We don't do automatic update of the `layout-grid-cells` property.
(remove #(= :layout-grid-cells %)))
(loop [attrs updatable-attrs
roperations []
uoperations '()]
@ -1730,6 +1733,47 @@
roperations'
uoperations')))))))
(defn update-attrs-on-switch
"Copy attributes that have changed in the origin shape to the dest shape. Used on variants switch"
[changes dest-shape origin-shape dest-root origin-root origin-ref-shape container]
(let [;; We need to sync only the position relative to the origin of the component.
;; (see update-attrs for a full explanation)
origin-shape (reposition-shape origin-shape origin-root dest-root)
touched (get dest-shape :touched #{})
touched-origin (get origin-shape :touched #{})]
(loop [attrs updatable-attrs
roperations [{:type :set-touched :touched (:touched origin-shape)}]
uoperations (list {:type :set-touched :touched (:touched dest-shape)})]
(if-let [attr (first attrs)]
(let [attr-group (get ctk/sync-attrs attr)
[roperations' uoperations']
(if (or
;; If the attribute is not valid for the destiny, don't copy it
(not (cts/is-allowed-attr? attr (:type dest-shape)))
;; If the values are already equal, don't copy it
(= (get origin-shape attr) (get dest-shape attr))
;; If the referenced shape on the original component doesn't have the same value, don't copy it
;; Exceptions: :points :selrect and :content can be different
(and
(not (contains? #{:points :selrect :content} attr))
(not= (get origin-ref-shape attr) (get dest-shape attr)))
;; The :content attr cant't be copied to elements of different type
(and (= attr :content) (not= (:type origin-shape) (:type dest-shape)))
;; If the attr is not touched in the origin shape, don't copy it
(not (touched-origin attr-group)))
[roperations uoperations]
(add-update-attr-operations attr dest-shape origin-shape roperations uoperations touched))]
(recur (next attrs)
roperations'
uoperations'))
(cond-> changes
(> (count roperations) 1)
(add-update-attr-changes dest-shape container roperations uoperations)
:always
(generate-update-tokens container dest-shape origin-shape touched false))))))
(defn- propagate-attrs
"Helper that puts the origin attributes (attrs) into dest but only if
not touched the group or if omit-touched? flag is true"

View file

@ -1,12 +1,30 @@
(ns app.common.logic.variants
(:require
[app.common.files.changes-builder :as pcb]
[app.common.files.helpers :as cfh]
[app.common.files.variant :as cfv]
[app.common.logic.libraries :as cll]
[app.common.logic.variant-properties :as clvp]
[app.common.types.components-list :as ctcl]
[app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.variant :as ctv]))
(defn- generate-path
[path objects base-id shape]
(let [get-type #(case %
:frame :container
:group :container
:rect :shape
:circle :shape
:bool :shape
:path :shape
%)]
(if (= base-id (:id shape))
path
(generate-path (str path " " (:name shape) (get-type (:type shape))) objects base-id (get objects (:parent-id shape))))))
(defn generate-add-new-variant
[changes shape variant-id new-component-id new-shape-id prop-num]
(let [data (pcb/get-library-data changes)
@ -28,3 +46,30 @@
(-> changes
(clvp/generate-update-property-value new-component-id prop-num value)
(pcb/change-parent (:parent-id shape) [new-shape] 0))))
(defn generate-keep-touched
[changes new-shape original-shape original-shapes page]
(let [data (pcb/get-library-data changes)
objects (pcb/get-objects changes)
orig-comp (ctcl/get-component data (:component-id original-shape) true)
new-path-map (into {}
(map (fn [shape] {(generate-path "" objects (:id new-shape) shape) shape}))
(cfh/get-children-with-self objects (:id new-shape)))
orig-touched (filter (comp seq :touched) original-shapes)
orig-objects (into {} (map (juxt :id identity) original-shapes))
container (ctn/make-container page :page)]
(reduce
(fn [changes touched-shape]
(let [path (generate-path "" orig-objects (:id original-shape) touched-shape)
related-shape (get new-path-map path)
orig-ref-shape (ctf/get-ref-shape data orig-comp touched-shape)]
(if related-shape
(cll/update-attrs-on-switch
changes related-shape touched-shape new-shape original-shape orig-ref-shape container)
changes)))
changes
orig-touched)))

View file

@ -397,6 +397,50 @@
(or (some :fill-image fills)
(some :stroke-image strokes)))
;; Valid attributes
(def ^:private allowed-shape-attrs #{:page-id :component-id :component-file :component-root :main-instance
:remote-synced :shape-ref :touched :blocked :collapsed :locked
:hidden :masked-group :fills :proportion :proportion-lock :constraints-h
:constraints-v :fixed-scroll :r1 :r2 :r3 :r4 :opacity :grids :exports
:strokes :blend-mode :interactions :shadow :blur :grow-type :applied-tokens
:plugin-data})
(def ^:private allowed-shape-geom-attrs #{:x :y :width :height})
(def ^:private allowed-shape-base-attrs #{:id :name :type :selrect :points :transform :transform-inverse :parent-id :frame-id})
(def ^:private allowed-bool-attrs #{:shapes :bool-type :content})
(def ^:private allowed-group-attrs #{:shapes})
(def ^:private allowed-frame-attrs #{:shapes :hide-fill-on-export :show-content :hide-in-viewer})
(def ^:private allowed-image-attrs #{:metadata})
(def ^:private allowed-svg-attrs #{:content})
(def ^:private allowed-path-attrs #{:content})
(def ^:private allowed-text-attrs #{:content})
(def ^:private allowed-generic-attrs (set/union allowed-shape-attrs allowed-shape-geom-attrs allowed-shape-base-attrs))
(defn is-allowed-attr?
[attr type]
(case type
:group (or (contains? allowed-group-attrs attr)
(contains? allowed-generic-attrs attr))
:frame (or (contains? allowed-frame-attrs attr)
(contains? allowed-generic-attrs attr))
:bool (or (contains? allowed-bool-attrs attr)
(contains? allowed-shape-attrs attr)
(contains? allowed-shape-base-attrs attr))
:rect (contains? allowed-generic-attrs attr)
:circle (contains? allowed-generic-attrs attr)
:image (or (contains? allowed-image-attrs attr)
(contains? allowed-generic-attrs attr))
:svg-raw (or (contains? allowed-svg-attrs attr)
(contains? allowed-generic-attrs attr))
:path (or (contains? allowed-path-attrs attr)
(contains? allowed-shape-attrs attr)
(contains? allowed-shape-base-attrs attr))
:text (or (contains? allowed-text-attrs attr)
(contains? allowed-generic-attrs attr))))
;; --- Initialization
(def ^:private minimal-rect-attrs

View file

@ -16,6 +16,7 @@
[app.common.logging :as log]
[app.common.logic.libraries :as cll]
[app.common.logic.shapes :as cls]
[app.common.logic.variants :as clv]
[app.common.types.color :as ctc]
[app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl]
@ -988,7 +989,7 @@
(defn component-swap
"Swaps a component with another one"
[shape file-id id-new-component]
[shape file-id id-new-component keep-touched?]
(dm/assert! (uuid? id-new-component))
(dm/assert! (uuid? file-id))
(ptk/reify ::component-swap
@ -1002,6 +1003,7 @@
parent (get objects (:parent-id shape))
ldata (dsh/lookup-file-data state file-id)
orig-shapes (when keep-touched? (cfh/get-children-with-self objects (:id shape)))
;; If the target parent is a grid layout we need to pass the target cell
target-cell (when (ctl/grid-layout? parent)
@ -1018,7 +1020,11 @@
[new-shape all-parents changes]
(-> (pcb/empty-changes it (:id page))
(pcb/set-undo-group undo-group)
(cll/generate-component-swap objects shape ldata page libraries id-new-component index target-cell keep-props-values))]
(cll/generate-component-swap objects shape ldata page libraries id-new-component index target-cell keep-props-values))
changes (if keep-touched?
(clv/generate-keep-touched changes new-shape shape orig-shapes page)
changes)]
(rx/of
(dwu/start-undo-transaction undo-id)
@ -1047,7 +1053,7 @@
:undo-id undo-id)
(rx/concat
(rx/of (dwu/start-undo-transaction undo-id))
(rx/map #(component-swap % file-id id-new-component) (rx/from shapes))
(rx/map #(component-swap % file-id id-new-component false) (rx/from shapes))
(rx/of (dwu/commit-undo-transaction undo-id))
(rx/of (dwsp/open-specialized-panel :component-swap)))))))

View file

@ -327,7 +327,7 @@
(filter #(= (dm/get-in % [:variant-properties pos :value]) val)))
nearest-comp (apply min-key #(ctv/distance target-props (:variant-properties %)) valid-comps)]
(when nearest-comp
(st/emit! (dwl/component-swap shape (:component-file shape) (:id nearest-comp))))))))]
(st/emit! (dwl/component-swap shape (:component-file shape) (:id nearest-comp) true)))))))]
[:*
(for [[pos prop] (map vector (range) properties)]