mirror of
https://github.com/penpot/penpot.git
synced 2025-07-23 00:17:28 +02:00
✨ Update colors and typographies inside components
This commit is contained in:
parent
b97bbd10f0
commit
272d023f3c
3 changed files with 116 additions and 154 deletions
|
@ -20,9 +20,10 @@
|
||||||
(update data :pages-index #(d/mapm f %)))
|
(update data :pages-index #(d/mapm f %)))
|
||||||
|
|
||||||
(defn select-objects
|
(defn select-objects
|
||||||
"Get a list of all objects in a page that satisfy a condition"
|
"Get a list of all objects in a container (a page or a component) that
|
||||||
[f page]
|
satisfy a condition"
|
||||||
(filter f (vals (get page :objects))))
|
[f container]
|
||||||
|
(filter f (vals (get container :objects))))
|
||||||
|
|
||||||
(defn update-object-list
|
(defn update-object-list
|
||||||
"Update multiple objects in a page at once"
|
"Update multiple objects in a page at once"
|
||||||
|
|
|
@ -450,12 +450,14 @@
|
||||||
|
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(watch [_ state stream]
|
||||||
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file-components state file-id)
|
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components file-id state)
|
||||||
[rchanges2 uchanges2] (dwlh/generate-sync-library-components state file-id)
|
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
|
||||||
[rchanges3 uchanges3] (dwlh/generate-sync-file :colors file-id state)
|
[rchanges3 uchanges3] (dwlh/generate-sync-file :colors file-id state)
|
||||||
[rchanges4 uchanges4] (dwlh/generate-sync-file :typographies file-id state)
|
[rchanges4 uchanges4] (dwlh/generate-sync-library :colors file-id state)
|
||||||
rchanges (d/concat rchanges1 rchanges2 rchanges3 rchanges4)
|
[rchanges5 uchanges5] (dwlh/generate-sync-file :typographies file-id state)
|
||||||
uchanges (d/concat uchanges1 uchanges2 uchanges3 uchanges4)]
|
[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/concat
|
||||||
(rx/of (dm/hide-tag :sync-dialog))
|
(rx/of (dm/hide-tag :sync-dialog))
|
||||||
(when rchanges
|
(when rchanges
|
||||||
|
@ -464,7 +466,7 @@
|
||||||
(rp/mutation :update-sync
|
(rp/mutation :update-sync
|
||||||
{:file-id (get-in state [:workspace-file :id])
|
{:file-id (get-in state [:workspace-file :id])
|
||||||
:library-id 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))))))))
|
(rx/of (sync-file-2nd-stage file-id))))))))
|
||||||
|
|
||||||
(defn sync-file-2nd-stage
|
(defn sync-file-2nd-stage
|
||||||
|
@ -481,7 +483,7 @@
|
||||||
(ptk/reify ::sync-file-2nd-stage
|
(ptk/reify ::sync-file-2nd-stage
|
||||||
ptk/WatchEvent
|
ptk/WatchEvent
|
||||||
(watch [_ state stream]
|
(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
|
(when rchanges
|
||||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
|
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
|
|
||||||
(defonce empty-changes [[] []])
|
(defonce empty-changes [[] []])
|
||||||
|
|
||||||
(declare generate-sync-page)
|
(declare generate-sync-container)
|
||||||
(declare generate-sync-shape)
|
(declare generate-sync-shape)
|
||||||
|
|
||||||
(declare generate-sync-component-components)
|
(declare generate-sync-component-components)
|
||||||
|
@ -30,9 +30,11 @@
|
||||||
(declare update-attrs)
|
(declare update-attrs)
|
||||||
(declare calc-new-pos)
|
(declare calc-new-pos)
|
||||||
|
|
||||||
|
;; ---- General library synchronization functions ----
|
||||||
|
|
||||||
(defn generate-sync-file
|
(defn generate-sync-file
|
||||||
"Generic method that given a type of asset will iterate through the file pages
|
"Generate changes to synchronize all shapes in all pages of the current file,
|
||||||
and call synchronize"
|
with the given asset of the given library."
|
||||||
[asset-type library-id state]
|
[asset-type library-id state]
|
||||||
|
|
||||||
(s/assert #{:colors :components :typographies} asset-type)
|
(s/assert #{:colors :components :typographies} asset-type)
|
||||||
|
@ -51,13 +53,47 @@
|
||||||
uchanges []]
|
uchanges []]
|
||||||
(if-let [page (first pages)]
|
(if-let [page (first pages)]
|
||||||
(let [[page-rchanges page-uchanges]
|
(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)
|
(recur (next pages)
|
||||||
(d/concat rchanges page-rchanges)
|
(d/concat rchanges page-rchanges)
|
||||||
(d/concat uchanges page-uchanges)))
|
(d/concat uchanges page-uchanges)))
|
||||||
[rchanges 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
|
(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]
|
[asset-type library-id]
|
||||||
(case asset-type
|
(case asset-type
|
||||||
:components
|
:components
|
||||||
|
@ -83,35 +119,54 @@
|
||||||
#(and (some? (:typography-ref-id %))
|
#(and (some? (:typography-ref-id %))
|
||||||
(= library-id (:typography-ref-file %)))))))))
|
(= library-id (:typography-ref-file %)))))))))
|
||||||
|
|
||||||
(defn generate-sync-page
|
(defn generate-sync-container
|
||||||
[asset-type library-id library-items page]
|
"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)
|
(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)
|
(loop [shapes (seq linked-shapes)
|
||||||
rchanges []
|
rchanges []
|
||||||
uchanges []]
|
uchanges []]
|
||||||
(if-let [shape (first shapes)]
|
(if-let [shape (first shapes)]
|
||||||
(let [[shape-rchanges shape-uchanges]
|
(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)
|
(recur (next shapes)
|
||||||
(d/concat rchanges shape-rchanges)
|
(d/concat rchanges shape-rchanges)
|
||||||
(d/concat uchanges shape-uchanges)))
|
(d/concat uchanges shape-uchanges)))
|
||||||
[rchanges uchanges]))))
|
[rchanges uchanges]))))
|
||||||
|
|
||||||
(defmulti generate-sync-shape (fn [type _ _ _ _ _] type))
|
(defmulti generate-sync-shape (fn [type _ _ _ _ _ _ _] type))
|
||||||
|
|
||||||
(defmethod generate-sync-shape :components
|
(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
|
(let [root-shape shape
|
||||||
objects (:objects page)
|
|
||||||
components library-items
|
components library-items
|
||||||
page-id nil
|
|
||||||
component-id (:id page)
|
|
||||||
reset-touched? false]
|
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
|
(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)
|
(loop [attrs (seq cp/color-sync-attrs)
|
||||||
roperations []
|
roperations []
|
||||||
uoperations []]
|
uoperations []]
|
||||||
|
@ -119,14 +174,16 @@
|
||||||
(if (nil? attr)
|
(if (nil? attr)
|
||||||
(if (empty? roperations)
|
(if (empty? roperations)
|
||||||
empty-changes
|
empty-changes
|
||||||
(let [rchanges [{:type :mod-obj
|
(let [rchanges [(d/without-nils {:type :mod-obj
|
||||||
:page-id (:id page)
|
:page-id page-id
|
||||||
|
:component-id component-id
|
||||||
:id (:id shape)
|
:id (:id shape)
|
||||||
:operations roperations}]
|
:operations roperations})]
|
||||||
uchanges [{:type :mod-obj
|
uchanges [(d/without-nils {:type :mod-obj
|
||||||
:page-id (:id page)
|
:page-id page-id
|
||||||
|
:component-id component-id
|
||||||
:id (:id shape)
|
:id (:id shape)
|
||||||
:operations uoperations}]]
|
:operations uoperations})]]
|
||||||
[rchanges uchanges]))
|
[rchanges uchanges]))
|
||||||
(let [attr-ref-id (keyword (str (name attr) "-ref-id"))]
|
(let [attr-ref-id (keyword (str (name attr) "-ref-id"))]
|
||||||
(if-not (contains? shape attr-ref-id)
|
(if-not (contains? shape attr-ref-id)
|
||||||
|
@ -147,29 +204,35 @@
|
||||||
(conj uoperations uoperation)))))))))
|
(conj uoperations uoperation)))))))))
|
||||||
|
|
||||||
(defmethod generate-sync-shape :typographies
|
(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]
|
(let [update-node (fn [node]
|
||||||
(if-let [typography (get library-items (:typography-ref-id node))]
|
(if-let [typography (get library-items (:typography-ref-id node))]
|
||||||
(merge node (d/without-keys typography [:name :id]))
|
(merge node (d/without-keys typography [:name :id]))
|
||||||
node))
|
node))
|
||||||
old-content (:content shape)
|
old-content (:content shape)
|
||||||
new-content (ut/map-node update-node old-content)
|
new-content (ut/map-node update-node old-content)
|
||||||
rchanges [{:type :mod-obj
|
rchanges [(d/without-nils {:type :mod-obj
|
||||||
:page-id (:id page)
|
:page-id page-id
|
||||||
|
:component-id component-id
|
||||||
:id (:id shape)
|
:id (:id shape)
|
||||||
:operations [{:type :set
|
:operations [{:type :set
|
||||||
:attr :content
|
:attr :content
|
||||||
:val new-content}]}]
|
:val new-content}]})]
|
||||||
lchanges [{:type :mod-obj
|
lchanges [(d/without-nils {:type :mod-obj
|
||||||
:page-id (:id page)
|
:page-id page-id
|
||||||
|
:component-id component-id
|
||||||
:id (:id shape)
|
:id (:id shape)
|
||||||
:operations [{:type :set
|
:operations [{:type :set
|
||||||
:attr :content
|
:attr :content
|
||||||
:val old-content}]}]]
|
:val old-content}]})]]
|
||||||
(if (= new-content old-content)
|
(if (= new-content old-content)
|
||||||
empty-changes
|
empty-changes
|
||||||
[rchanges lchanges])))
|
[rchanges lchanges])))
|
||||||
|
|
||||||
|
|
||||||
;; ---- Create a new component ----
|
;; ---- Create a new component ----
|
||||||
|
|
||||||
(defn make-component-shape
|
(defn make-component-shape
|
||||||
|
@ -199,118 +262,14 @@
|
||||||
(cph/clone-object shape nil objects update-new-shape update-original-shape)))
|
(cph/clone-object shape nil objects update-new-shape update-original-shape)))
|
||||||
|
|
||||||
|
|
||||||
;; ---- Synchronize shapes with components
|
;; ---- Component synchronization helpers ----
|
||||||
|
|
||||||
(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))))))))
|
|
||||||
|
|
||||||
|
|
||||||
(defn generate-sync-shape-and-children-components
|
(defn generate-sync-shape-and-children-components
|
||||||
"Generate changes to synchronize one shape that is linked to a component,
|
"Generate changes to synchronize one shape that is linked to a component,
|
||||||
and all its children. If reset-touched? is false, same considerations as
|
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
|
in generate-sync-shape :components. If it's true, all attributes of the
|
||||||
will be copied, and the 'touched' flags in the shapes will be cleared."
|
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?]
|
[root-shape objects components page-id component-id reset-touched?]
|
||||||
(let [all-shapes (cph/get-object-with-children (:id root-shape) objects)
|
(let [all-shapes (cph/get-object-with-children (:id root-shape) objects)
|
||||||
component (get components (:component-id root-shape))
|
component (get components (:component-id root-shape))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue