0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-24 15:39:50 -05:00

Update colors and typographies inside components

This commit is contained in:
Andrés Moya 2020-10-02 14:58:49 +02:00 committed by Alonso Torres
parent b97bbd10f0
commit 272d023f3c
3 changed files with 116 additions and 154 deletions

View file

@ -20,9 +20,10 @@
(update data :pages-index #(d/mapm f %)))
(defn select-objects
"Get a list of all objects in a page that satisfy a condition"
[f page]
(filter f (vals (get page :objects))))
"Get a list of all objects in a container (a page or a component) that
satisfy a condition"
[f container]
(filter f (vals (get container :objects))))
(defn update-object-list
"Update multiple objects in a page at once"

View file

@ -450,12 +450,14 @@
ptk/WatchEvent
(watch [_ state stream]
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file-components state file-id)
[rchanges2 uchanges2] (dwlh/generate-sync-library-components state file-id)
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components file-id state)
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
[rchanges3 uchanges3] (dwlh/generate-sync-file :colors file-id state)
[rchanges4 uchanges4] (dwlh/generate-sync-file :typographies file-id state)
rchanges (d/concat rchanges1 rchanges2 rchanges3 rchanges4)
uchanges (d/concat uchanges1 uchanges2 uchanges3 uchanges4)]
[rchanges4 uchanges4] (dwlh/generate-sync-library :colors file-id state)
[rchanges5 uchanges5] (dwlh/generate-sync-file :typographies file-id state)
[rchanges6 uchanges6] (dwlh/generate-sync-library :typographies file-id state)
rchanges (d/concat rchanges1 rchanges2 rchanges3 rchanges4 rchanges5 rchanges6)
uchanges (d/concat uchanges1 uchanges2 uchanges3 uchanges4 uchanges5 uchanges6)]
(rx/concat
(rx/of (dm/hide-tag :sync-dialog))
(when rchanges
@ -464,7 +466,7 @@
(rp/mutation :update-sync
{:file-id (get-in state [:workspace-file :id])
:library-id file-id}))
(when (seq rchanges2)
(when (or (seq rchanges2) (seq rchanges4) (seq rchanges6))
(rx/of (sync-file-2nd-stage file-id))))))))
(defn sync-file-2nd-stage
@ -481,7 +483,7 @@
(ptk/reify ::sync-file-2nd-stage
ptk/WatchEvent
(watch [_ state stream]
(let [[rchanges uchanges] (dwlh/generate-sync-file-components state nil)]
(let [[rchanges uchanges] (dwlh/generate-sync-file :components nil state)]
(when rchanges
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))

View file

@ -19,7 +19,7 @@
(defonce empty-changes [[] []])
(declare generate-sync-page)
(declare generate-sync-container)
(declare generate-sync-shape)
(declare generate-sync-component-components)
@ -30,9 +30,11 @@
(declare update-attrs)
(declare calc-new-pos)
;; ---- General library synchronization functions ----
(defn generate-sync-file
"Generic method that given a type of asset will iterate through the file pages
and call synchronize"
"Generate changes to synchronize all shapes in all pages of the current file,
with the given asset of the given library."
[asset-type library-id state]
(s/assert #{:colors :components :typographies} asset-type)
@ -51,13 +53,47 @@
uchanges []]
(if-let [page (first pages)]
(let [[page-rchanges page-uchanges]
(generate-sync-page asset-type library-id library-items page)]
(generate-sync-container asset-type
library-id
library-items
page
(:id page)
nil)]
(recur (next pages)
(d/concat rchanges page-rchanges)
(d/concat uchanges page-uchanges)))
[rchanges uchanges])))))
(defn generate-sync-library
"Generate changes to synchronize all shapes inside components of the current
file library, that use the given type of asset of the given library."
[asset-type library-id state]
(let [library-items
(if (nil? library-id)
(get-in state [:workspace-data asset-type])
(get-in state [:workspace-libraries library-id :data asset-type]))]
(if (empty? library-items)
empty-changes
(loop [local-components (seq (vals (get-in state [:workspace-data :components])))
rchanges []
uchanges []]
(if-let [local-component (first local-components)]
(let [[comp-rchanges comp-uchanges]
(generate-sync-container asset-type
library-id
library-items
local-component
nil
(:id local-component))]
(recur (next local-components)
(d/concat rchanges comp-rchanges)
(d/concat uchanges comp-uchanges)))
[rchanges uchanges])))))
(defn has-asset-reference-fn
"Gets a function that checks if a shape uses some asset of the given type
in the given library."
[asset-type library-id]
(case asset-type
:components
@ -83,35 +119,54 @@
#(and (some? (:typography-ref-id %))
(= library-id (:typography-ref-file %)))))))))
(defn generate-sync-page
[asset-type library-id library-items page]
(defn generate-sync-container
"Generate changes to synchronize all shapes in a particular container
(a page or a component)."
[asset-type library-id library-items container page-id component-id]
(let [has-asset-reference? (has-asset-reference-fn asset-type library-id)
linked-shapes (cph/select-objects has-asset-reference? page)]
linked-shapes (cph/select-objects has-asset-reference? container)]
(loop [shapes (seq linked-shapes)
rchanges []
uchanges []]
(if-let [shape (first shapes)]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape asset-type library-id library-items page shape)]
(generate-sync-shape asset-type
library-id
library-items
(get container :objects)
page-id
component-id
shape)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges)))
[rchanges uchanges]))))
(defmulti generate-sync-shape (fn [type _ _ _ _ _] type))
(defmulti generate-sync-shape (fn [type _ _ _ _ _ _ _] type))
(defmethod generate-sync-shape :components
[_ library-id library-items page shape]
[_ library-id library-items objects page-id component-id shape]
;; Synchronize a shape that is the root instance of a component, and all of its
;; children. All attributes of the component shape that have changed, and whose
;; group have not been touched in the linked shape, will be copied to the shape.
;; Any shape that is linked to a no-longer existent component shape will be
;; detached.
(let [root-shape shape
objects (:objects page)
components library-items
page-id nil
component-id (:id page)
reset-touched? false]
(generate-sync-shape-and-children-components root-shape objects components page-id component-id reset-touched?)))
(generate-sync-shape-and-children-components root-shape
objects
components
page-id
component-id
reset-touched?)))
(defmethod generate-sync-shape :colors
[_ library-id library-items page shape]
[_ library-id library-items _ page-id component-id shape]
;; Synchronize a shape that uses some colors of the library. The value of the
;; color in the library is copied to the shape.
(loop [attrs (seq cp/color-sync-attrs)
roperations []
uoperations []]
@ -119,14 +174,16 @@
(if (nil? attr)
(if (empty? roperations)
empty-changes
(let [rchanges [{:type :mod-obj
:page-id (:id page)
:id (:id shape)
:operations roperations}]
uchanges [{:type :mod-obj
:page-id (:id page)
:id (:id shape)
:operations uoperations}]]
(let [rchanges [(d/without-nils {:type :mod-obj
:page-id page-id
:component-id component-id
:id (:id shape)
:operations roperations})]
uchanges [(d/without-nils {:type :mod-obj
:page-id page-id
:component-id component-id
:id (:id shape)
:operations uoperations})]]
[rchanges uchanges]))
(let [attr-ref-id (keyword (str (name attr) "-ref-id"))]
(if-not (contains? shape attr-ref-id)
@ -147,29 +204,35 @@
(conj uoperations uoperation)))))))))
(defmethod generate-sync-shape :typographies
[_ library-id library-items page shape]
[_ library-id library-items _ page-id component-id shape]
;; Synchronize a shape that uses some typographies of the library. The attributes
;; of the typography are copied to the shape."
(let [update-node (fn [node]
(if-let [typography (get library-items (:typography-ref-id node))]
(merge node (d/without-keys typography [:name :id]))
node))
old-content (:content shape)
new-content (ut/map-node update-node old-content)
rchanges [{:type :mod-obj
:page-id (:id page)
:id (:id shape)
:operations [{:type :set
:attr :content
:val new-content}]}]
lchanges [{:type :mod-obj
:page-id (:id page)
:id (:id shape)
:operations [{:type :set
:attr :content
:val old-content}]}]]
rchanges [(d/without-nils {:type :mod-obj
:page-id page-id
:component-id component-id
:id (:id shape)
:operations [{:type :set
:attr :content
:val new-content}]})]
lchanges [(d/without-nils {:type :mod-obj
:page-id page-id
:component-id component-id
:id (:id shape)
:operations [{:type :set
:attr :content
:val old-content}]})]]
(if (= new-content old-content)
empty-changes
[rchanges lchanges])))
;; ---- Create a new component ----
(defn make-component-shape
@ -199,118 +262,14 @@
(cph/clone-object shape nil objects update-new-shape update-original-shape)))
;; ---- Synchronize shapes with components
(declare generate-sync-page-components)
(defn generate-sync-file-components
"Generate changes to synchronize all shapes in current file that are linked
to some component in the given library. All attributes of the components
that have changed, and whose group have not been touched in the linked shape,
will be copied to the shape. Any shape that is linked to a no-longer
existent component will be detached."
[state library-id]
(let [components
(if (nil? library-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries library-id :data :components]))]
(if (nil? components)
[[] []]
(loop [pages (seq (vals (get-in state [:workspace-data :pages-index])))
rchanges []
uchanges []]
(let [page (first pages)]
(if (nil? page)
[rchanges uchanges]
(let [[page-rchanges page-uchanges]
(generate-sync-page-components page library-id components)]
(recur (next pages)
(d/concat rchanges page-rchanges)
(d/concat uchanges page-uchanges)))))))))
(defn generate-sync-page-components
"Generate changes to synchronize all shapes in a particular page.
Same considerations as above."
[page library-id components]
(let [objects (get page :objects)
linked-shapes (cph/select-objects #(and (some? (:component-id %))
(= (:component-file %) library-id))
page)]
(loop [shapes (seq linked-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape-and-children-components shape
objects
components
(:id page)
nil
false)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
(defn generate-sync-library-components
"Generate changes to synchronize all shapes inside components of the current
file library, that are linked to other component in the given library.
Same considerations as above."
[state library-id]
(let [components
(if (nil? library-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries library-id :data :components]))]
(if (nil? components)
empty-changes
(loop [local-components (seq (vals (get-in state [:workspace-data :components])))
rchanges []
uchanges []]
(let [local-component (first local-components)]
(if (nil? local-component)
[rchanges uchanges]
(let [[comp-rchanges comp-uchanges]
(generate-sync-component-components
local-component library-id components)]
(recur (next local-components)
(d/concat rchanges comp-rchanges)
(d/concat uchanges comp-uchanges)))))))))
(defn generate-sync-component-components
"Generate changes to synchronize all shapes in a particular component.
Same considerations as above."
[local-component library-id components]
(let [objects (get local-component :objects)
linked-shapes (filter #(and (some? (:component-id %))
(= (:component-file %) library-id))
(vals objects))]
(loop [shapes (seq linked-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape-and-children-components shape
objects
components
nil
(:id local-component)
false)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
;; ---- Component synchronization helpers ----
(defn generate-sync-shape-and-children-components
"Generate changes to synchronize one shape that is linked to a component,
and all its children. If reset-touched? is false, same considerations as
above. If it's true, all attributes of the component that have changed
will be copied, and the 'touched' flags in the shapes will be cleared."
in generate-sync-shape :components. If it's true, all attributes of the
component that have changed will be copied, and the 'touched' flags in
the shapes will be cleared."
[root-shape objects components page-id component-id reset-touched?]
(let [all-shapes (cph/get-object-with-children (:id root-shape) objects)
component (get components (:component-id root-shape))