mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 12:21:38 +02:00
✨ Manage overrides on variants switch
This commit is contained in:
parent
ea095a98ba
commit
751bed4117
5 changed files with 153 additions and 14 deletions
|
@ -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"
|
||||
|
|
|
@ -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)))
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)))))))
|
||||
|
||||
|
|
|
@ -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)]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue