mirror of
https://github.com/penpot/penpot.git
synced 2025-06-01 22:21:50 +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.file :as ctf]
|
||||||
[app.common.types.page :as ctp]
|
[app.common.types.page :as ctp]
|
||||||
[app.common.types.pages-list :as ctpl]
|
[app.common.types.pages-list :as ctpl]
|
||||||
|
[app.common.types.shape :as cts]
|
||||||
[app.common.types.shape-tree :as ctst]
|
[app.common.types.shape-tree :as ctst]
|
||||||
[app.common.types.shape.interactions :as ctsi]
|
[app.common.types.shape.interactions :as ctsi]
|
||||||
[app.common.types.shape.layout :as ctl]
|
[app.common.types.shape.layout :as ctl]
|
||||||
|
@ -43,6 +44,12 @@
|
||||||
(def log-shape-ids #{})
|
(def log-shape-ids #{})
|
||||||
(def log-container-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?
|
(defn enabled-shape?
|
||||||
[id container]
|
[id container]
|
||||||
(or (empty? log-shape-ids)
|
(or (empty? log-shape-ids)
|
||||||
|
@ -1704,11 +1711,7 @@
|
||||||
origin-shape (reposition-shape origin-shape origin-root dest-root)
|
origin-shape (reposition-shape origin-shape origin-root dest-root)
|
||||||
touched (get dest-shape :touched #{})]
|
touched (get dest-shape :touched #{})]
|
||||||
|
|
||||||
(loop [attrs (->> (seq (keys ctk/sync-attrs))
|
(loop [attrs updatable-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 %)))
|
|
||||||
roperations []
|
roperations []
|
||||||
uoperations '()]
|
uoperations '()]
|
||||||
|
|
||||||
|
@ -1730,6 +1733,47 @@
|
||||||
roperations'
|
roperations'
|
||||||
uoperations')))))))
|
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
|
(defn- propagate-attrs
|
||||||
"Helper that puts the origin attributes (attrs) into dest but only if
|
"Helper that puts the origin attributes (attrs) into dest but only if
|
||||||
not touched the group or if omit-touched? flag is true"
|
not touched the group or if omit-touched? flag is true"
|
||||||
|
|
|
@ -1,12 +1,30 @@
|
||||||
(ns app.common.logic.variants
|
(ns app.common.logic.variants
|
||||||
(:require
|
(:require
|
||||||
[app.common.files.changes-builder :as pcb]
|
[app.common.files.changes-builder :as pcb]
|
||||||
|
[app.common.files.helpers :as cfh]
|
||||||
[app.common.files.variant :as cfv]
|
[app.common.files.variant :as cfv]
|
||||||
[app.common.logic.libraries :as cll]
|
[app.common.logic.libraries :as cll]
|
||||||
[app.common.logic.variant-properties :as clvp]
|
[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]))
|
[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
|
(defn generate-add-new-variant
|
||||||
[changes shape variant-id new-component-id new-shape-id prop-num]
|
[changes shape variant-id new-component-id new-shape-id prop-num]
|
||||||
(let [data (pcb/get-library-data changes)
|
(let [data (pcb/get-library-data changes)
|
||||||
|
@ -28,3 +46,30 @@
|
||||||
(-> changes
|
(-> changes
|
||||||
(clvp/generate-update-property-value new-component-id prop-num value)
|
(clvp/generate-update-property-value new-component-id prop-num value)
|
||||||
(pcb/change-parent (:parent-id shape) [new-shape] 0))))
|
(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)
|
(or (some :fill-image fills)
|
||||||
(some :stroke-image strokes)))
|
(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
|
;; --- Initialization
|
||||||
|
|
||||||
(def ^:private minimal-rect-attrs
|
(def ^:private minimal-rect-attrs
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
[app.common.logging :as log]
|
[app.common.logging :as log]
|
||||||
[app.common.logic.libraries :as cll]
|
[app.common.logic.libraries :as cll]
|
||||||
[app.common.logic.shapes :as cls]
|
[app.common.logic.shapes :as cls]
|
||||||
|
[app.common.logic.variants :as clv]
|
||||||
[app.common.types.color :as ctc]
|
[app.common.types.color :as ctc]
|
||||||
[app.common.types.component :as ctk]
|
[app.common.types.component :as ctk]
|
||||||
[app.common.types.components-list :as ctkl]
|
[app.common.types.components-list :as ctkl]
|
||||||
|
@ -988,7 +989,7 @@
|
||||||
|
|
||||||
(defn component-swap
|
(defn component-swap
|
||||||
"Swaps a component with another one"
|
"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? id-new-component))
|
||||||
(dm/assert! (uuid? file-id))
|
(dm/assert! (uuid? file-id))
|
||||||
(ptk/reify ::component-swap
|
(ptk/reify ::component-swap
|
||||||
|
@ -996,12 +997,13 @@
|
||||||
(watch [it state _]
|
(watch [it state _]
|
||||||
;; First delete shapes so we have space in the layout otherwise we can have problems
|
;; First delete shapes so we have space in the layout otherwise we can have problems
|
||||||
;; in the grid creating new rows/columns to make space
|
;; in the grid creating new rows/columns to make space
|
||||||
(let [libraries (dsh/lookup-libraries state)
|
(let [libraries (dsh/lookup-libraries state)
|
||||||
page (dsh/lookup-page state)
|
page (dsh/lookup-page state)
|
||||||
objects (:objects page)
|
objects (:objects page)
|
||||||
parent (get objects (:parent-id shape))
|
parent (get objects (:parent-id shape))
|
||||||
|
|
||||||
ldata (dsh/lookup-file-data state file-id)
|
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
|
;; If the target parent is a grid layout we need to pass the target cell
|
||||||
target-cell (when (ctl/grid-layout? parent)
|
target-cell (when (ctl/grid-layout? parent)
|
||||||
|
@ -1018,7 +1020,11 @@
|
||||||
[new-shape all-parents changes]
|
[new-shape all-parents changes]
|
||||||
(-> (pcb/empty-changes it (:id page))
|
(-> (pcb/empty-changes it (:id page))
|
||||||
(pcb/set-undo-group undo-group)
|
(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
|
(rx/of
|
||||||
(dwu/start-undo-transaction undo-id)
|
(dwu/start-undo-transaction undo-id)
|
||||||
|
@ -1047,7 +1053,7 @@
|
||||||
:undo-id undo-id)
|
:undo-id undo-id)
|
||||||
(rx/concat
|
(rx/concat
|
||||||
(rx/of (dwu/start-undo-transaction undo-id))
|
(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 (dwu/commit-undo-transaction undo-id))
|
||||||
(rx/of (dwsp/open-specialized-panel :component-swap)))))))
|
(rx/of (dwsp/open-specialized-panel :component-swap)))))))
|
||||||
|
|
||||||
|
|
|
@ -327,7 +327,7 @@
|
||||||
(filter #(= (dm/get-in % [:variant-properties pos :value]) val)))
|
(filter #(= (dm/get-in % [:variant-properties pos :value]) val)))
|
||||||
nearest-comp (apply min-key #(ctv/distance target-props (:variant-properties %)) valid-comps)]
|
nearest-comp (apply min-key #(ctv/distance target-props (:variant-properties %)) valid-comps)]
|
||||||
(when nearest-comp
|
(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)]
|
(for [[pos prop] (map vector (range) properties)]
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue