diff --git a/common/src/app/common/files/changes.cljc b/common/src/app/common/files/changes.cljc index 45195c48d3..50411a0144 100644 --- a/common/src/app/common/files/changes.cljc +++ b/common/src/app/common/files/changes.cljc @@ -1153,7 +1153,7 @@ ; We need to trigger a sync if the shape has changed any ; attribute that participates in components synchronization. (and (= (:type operation) :set) - (get ctk/sync-attrs (:attr operation)))) + (ctk/component-attr? (:attr operation)))) any-sync? (some need-sync? operations)] (when any-sync? (if page-id diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index bc9ab68b02..98db438160 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -27,6 +27,7 @@ [app.common.types.shape-tree :as ctst] [app.common.types.shape.interactions :as ctsi] [app.common.types.shape.layout :as ctl] + [app.common.types.token :as cto] [app.common.types.typography :as cty] [app.common.uuid :as uuid] [clojure.set :as set] @@ -1479,6 +1480,44 @@ [{:type :set-remote-synced :remote-synced (:remote-synced shape)}]})))))) +(defn- update-tokens + "Token synchronization algorithm. Copy the applied tokens that have changed + in the origin shape to the dest shape (applying or removing as necessary). + + Only the given token attributes are synced." + [changes container dest-shape orig-shape token-attrs] + (let [orig-tokens (get orig-shape :applied-tokens {}) + dest-tokens (get dest-shape :applied-tokens {}) + dest-tokens' (reduce (fn [dest-tokens' token-attr] + (let [orig-token (get orig-tokens token-attr) + dest-token (get dest-tokens token-attr)] + (if (= orig-token dest-token) + dest-tokens' + (if (nil? orig-token) + (dissoc dest-tokens' token-attr) + (assoc dest-tokens' token-attr orig-token))))) + dest-tokens + token-attrs)] + (if (= dest-tokens dest-tokens') + changes + (-> changes + (update :redo-changes conj (make-change + container + {:type :mod-obj + :id (:id dest-shape) + :operations [{:type :set + :attr :applied-tokens + :val dest-tokens' + :ignore-touched true}]})) + (update :undo-changes conj (make-change + container + {:type :mod-obj + :id (:id dest-shape) + :operations [{:type :set + :attr :applied-tokens + :val dest-tokens + :ignore-touched true}]})))))) + (defn- update-attrs "The main function that implements the attribute sync algorithm. Copy attributes that have changed in the origin shape to the dest shape. @@ -1511,37 +1550,41 @@ (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 %))) + applied-tokens #{} roperations [] uoperations '()] (let [attr (first attrs)] (if (nil? attr) - (if (empty? roperations) + (if (and (empty? roperations) (empty? applied-tokens)) changes (let [all-parents (cfh/get-parent-ids (:objects container) (:id dest-shape))] - (-> changes - (update :redo-changes conj (make-change - container - {:type :mod-obj - :id (:id dest-shape) - :operations roperations})) - (update :redo-changes conj (make-change - container - {:type :reg-objects - :shapes all-parents})) - (update :undo-changes conj (make-change - container - {:type :mod-obj - :id (:id dest-shape) - :operations (vec uoperations)})) - (update :undo-changes concat [(make-change - container - {:type :reg-objects - :shapes all-parents})])))) + (cond-> changes + (seq roperations) + (-> (update :redo-changes conj (make-change + container + {:type :mod-obj + :id (:id dest-shape) + :operations roperations})) + (update :redo-changes conj (make-change + container + {:type :reg-objects + :shapes all-parents})) + (update :undo-changes conj (make-change + container + {:type :mod-obj + :id (:id dest-shape) + :operations (vec uoperations)})) + (update :undo-changes concat [(make-change + container + {:type :reg-objects + :shapes all-parents})])) + (seq applied-tokens) + (update-tokens container dest-shape origin-shape applied-tokens)))) + (let [;; position-data is a special case because can be affected by :geometry-group and :content-group ;; so, if the position-data changes but the geometry is touched we need to reset the position-data ;; so it's calculated again @@ -1564,14 +1607,21 @@ :val (get dest-shape attr) :ignore-touched true} - attr-group (get ctk/sync-attrs attr)] + attr-group (get ctk/sync-attrs attr) + token-attrs (cto/shape-attr->token-attrs attr) + applied-tokens' (cond-> applied-tokens + (not (and (touched attr-group) + omit-touched?)) + (into token-attrs))] (if (or (= (get origin-shape attr) (get dest-shape attr)) (and (touched attr-group) omit-touched?)) (recur (next attrs) + applied-tokens' roperations uoperations) (recur (next attrs) + applied-tokens' (conj roperations roperation) (conj uoperations uoperation))))))))) diff --git a/common/src/app/common/types/component.cljc b/common/src/app/common/types/component.cljc index 2af68c8d89..fc4ea0093d 100644 --- a/common/src/app/common/types/component.cljc +++ b/common/src/app/common/types/component.cljc @@ -140,6 +140,14 @@ :layout-item-z-index :layout-item-align-self}) +(defn component-attr? + "Check if some attribute is one that is involved in component syncrhonization. + Note that design tokens also are involved, although they go by an alternate + route and thus they are not part of :sync-attrs." + [attr] + (or (get sync-attrs attr) + (= :applied-tokens attr))) + (defn instance-root? "Check if this shape is the head of a top instance." [shape] diff --git a/common/src/app/common/types/token.cljc b/common/src/app/common/types/token.cljc index 1075ba461f..d97279fb66 100644 --- a/common/src/app/common/types/token.cljc +++ b/common/src/app/common/types/token.cljc @@ -163,3 +163,14 @@ ::spacing ::rotation ::dimensions]) + +(defn shape-attr->token-attrs + [shape-attr] + (cond + (= :fills shape-attr) #{:fill} + (= :strokes shape-attr) #{:stroke-color :stroke-width} + (border-radius-keys shape-attr) #{shape-attr} + (sizing-keys shape-attr) #{shape-attr} + (opacity-keys shape-attr) #{shape-attr} + (spacing-keys shape-attr) #{shape-attr} + (rotation-keys shape-attr) #{shape-attr}))