0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-13 16:21:57 -05:00

Enhance synchronization of a component with subcomponents

This commit is contained in:
Andrés Moya 2020-10-06 15:44:42 +02:00
parent 7c75b75f5b
commit 68ca44188c
8 changed files with 346 additions and 239 deletions

View file

@ -236,12 +236,8 @@
:width :size-group
:height :size-group
:proportion :size-group
:x :position-group
:y :position-group
:rx :radius-group
:ry :radius-group
:points :points-group
:transform :transform-group})
:ry :radius-group})
(def color-sync-attrs [:fill-color
:stroke-color])
@ -892,21 +888,21 @@
(defmethod process-operation :set
[shape op]
(let [attr (:attr op)
val (:val op)
ignore (:ignore-touched op)
(let [attr (:attr op)
val (:val op)
ignore (:ignore-touched op)
shape-ref (:shape-ref shape)
group (get component-sync-attrs attr)]
group (get component-sync-attrs attr)]
(cond-> shape
(and shape-ref group (not ignore) (not= val (get shape attr)))
(update :touched #(conj (or % #{}) group))
(nil? val)
(dissoc attr)
(some? val)
(assoc attr val)
(and shape-ref group (not ignore))
(update :touched #(conj (or % #{}) group)))))
(assoc attr val))))
(defmethod process-operation :set-touched
[shape op]

View file

@ -31,15 +31,15 @@
(update page :objects
#(into % (d/index-by :id objects-list))))
(defn get-root-component
"Get the root shape linked to the component for this shape, if any"
[id objects]
(let [obj (get objects id)]
(if-let [component-id (:component-root? obj)]
id
(if-let [parent-id (:parent-id obj)]
(get-root-component parent-id objects)
nil))))
(defn get-root-shape
"Get the root shape linked to a component for this shape, if any"
[shape objects]
(if (:component-root? shape)
shape
(if-let [parent-id (:parent-id shape)]
(get-root-shape (get objects (:parent-id shape))
objects)
nil)))
(defn get-children
"Retrieve all children ids recursively for a given object"
@ -58,7 +58,7 @@
(defn get-object-with-children
"Retrieve a list with an object and all of its children"
[id objects]
(map #(get objects %) (concat [id] (get-children id objects))))
(map #(get objects %) (d/concat [id] (get-children id objects))))
(defn is-shape-grouped
"Checks if a shape is inside a group"

View file

@ -1148,7 +1148,6 @@
(update [_ state]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
root-id (cph/get-root-component (:id shape) objects)
mdata {:position position
:shape shape

View file

@ -160,7 +160,10 @@
:val (:component-root? updated-shape)}
{:type :set
:attr :shape-ref
:val (:shape-ref updated-shape)}]})
:val (:shape-ref updated-shape)}
{:type :set
:attr :touched
:val (:touched updated-shape)}]})
updated-shapes))
uchanges (conj uchanges
@ -184,9 +187,13 @@
:val (:component-root? original-shape)}
{:type :set
:attr :shape-ref
:val (:shape-ref original-shape)}]}))
:val (:shape-ref original-shape)}
{:type :set
:attr :touched
:val (:touched original-shape)}]}))
updated-shapes))]
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
(dws/select-shapes (d/ordered-set (:id group))))))))))
@ -245,7 +252,8 @@
(dwc/calculate-frame-overlap all-frames $))
(assoc $ :parent-id
(or (:parent-id $) (:frame-id $)))
(assoc $ :shape-ref (:id original-shape)))
(assoc $ :shape-ref (:id original-shape))
(dissoc $ :touched))
(nil? (:parent-id original-shape))
(assoc :component-id (:id original-shape)
@ -356,25 +364,35 @@
(ptk/reify ::reset-component
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
page (get-in state [:workspace-data :pages-index page-id])
objects (dwc/lookup-page-objects state page-id)
root-shape (get objects id)
file-id (get root-shape :component-file)
(js/console.info "##### RESET-COMPONENT of shape" (str id))
(let [page-id (:current-page-id state)
page (get-in state [:workspace-data :pages-index page-id])
objects (dwc/lookup-page-objects state page-id)
shape (get objects id)
file-id (get shape :component-file)
components
(if (nil? file-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries file-id :data :components]))
[all-shapes component root-component]
(dwlh/resolve-shapes-and-components shape
objects
state
true)
_ (js/console.info "shape" (:name shape) "<- component" (:name component))
_ (js/console.debug "all-shapes" (clj->js all-shapes))
_ (js/console.debug "component" (clj->js component))
_ (js/console.debug "root-component" (clj->js root-component))
[rchanges uchanges]
(dwlh/generate-sync-shape-and-children-components root-shape
objects
components
(dwlh/generate-sync-shape-and-children-components shape
all-shapes
component
root-component
(:id page)
nil
true)]
(js/console.debug "rchanges" (clj->js rchanges))
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
(defn update-component
@ -383,22 +401,32 @@
(ptk/reify ::update-component
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
root-shape (get objects id)
file-id (get root-shape :component-file)
(js/console.info "##### UPDATE-COMPONENT of shape" (str id))
(let [page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
shape (get objects id)
file-id (get shape :component-file)
components
(if (nil? file-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries file-id :data :components]))
[all-shapes component root-component]
(dwlh/resolve-shapes-and-components shape
objects
state
true)
_ (js/console.info "shape" (:name shape) "-> component" (:name component))
_ (js/console.debug "all-shapes" (clj->js all-shapes))
_ (js/console.debug "component" (clj->js component))
_ (js/console.debug "root-component" (clj->js root-component))
[rchanges uchanges]
(dwlh/generate-sync-shape-inverse root-shape
objects
components
(dwlh/generate-sync-shape-inverse shape
all-shapes
component
root-component
page-id)]
(js/console.debug "rchanges" (clj->js rchanges))
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true}))))))
(declare sync-file-2nd-stage)
@ -415,6 +443,7 @@
ptk/WatchEvent
(watch [_ state stream]
(js/console.info "##### SYNC-FILE" (str (or file-id "local")))
(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)
@ -423,6 +452,7 @@
[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)]
(js/console.debug "rchanges" (clj->js rchanges))
(rx/concat
(rx/of (dm/hide-tag :sync-dialog))
(when rchanges
@ -448,11 +478,13 @@
(ptk/reify ::sync-file-2nd-stage
ptk/WatchEvent
(watch [_ state stream]
(js/console.info "##### SYNC-FILE" (str (or file-id "local")) "(2nd stage)")
(let [[rchanges1 uchanges1] (dwlh/generate-sync-file :components nil state)
[rchanges2 uchanges2] (dwlh/generate-sync-library :components file-id state)
rchanges (d/concat rchanges1 rchanges2)
uchanges (d/concat uchanges1 uchanges2)]
(when rchanges
(js/console.debug "rchanges" (clj->js rchanges))
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))))
(def ignore-sync

View file

@ -21,8 +21,10 @@
(declare generate-sync-container)
(declare generate-sync-shape)
(declare has-asset-reference-fn)
(declare generate-sync-component-components)
(declare get-assets)
(declare resolve-shapes-and-components)
(declare generate-sync-shape-and-children-components)
(declare generate-sync-shape-inverse)
(declare generate-sync-shape<-component)
@ -50,7 +52,10 @@
(assoc :frame-id nil)
(nil? (:parent-id new-shape))
(assoc :component-root? true)))
(dissoc :component-id
:component-file
:component-root?
:shape-ref)))
;; Make the original shape an instance of the new component.
;; If one of the original shape children already was a component
@ -58,7 +63,8 @@
update-original-shape (fn [original-shape new-shape]
(cond-> original-shape
true
(assoc :shape-ref (:id new-shape))
(-> (assoc :shape-ref (:id new-shape))
(dissoc :touched))
(nil? (:parent-id new-shape))
(assoc :component-id (:id new-shape)
@ -96,7 +102,7 @@
(let [[page-rchanges page-uchanges]
(generate-sync-container asset-type
library-id
library-items
state
page
(:id page)
nil)]
@ -123,7 +129,7 @@
(let [[comp-rchanges comp-uchanges]
(generate-sync-container asset-type
library-id
library-items
state
local-component
nil
(:id local-component))]
@ -132,7 +138,30 @@
(d/concat uchanges comp-uchanges)))
[rchanges uchanges])))))
(defn has-asset-reference-fn
(defn- generate-sync-container
"Generate changes to synchronize all shapes in a particular container
(a page or a component) that are linked to the given library."
[asset-type library-id state 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? 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
state
(get container :objects)
page-id
component-id
shape)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-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]
@ -167,50 +196,28 @@
#(and (some? (:typography-ref-id %))
(= library-id (:typography-ref-file %)))))))))
(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? 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
(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
"Generate changes to synchronize one shape, that use the given type
of asset of the given library."
(fn [type _ _ _ _ _ _ _] type))
(defmethod generate-sync-shape :components
[_ library-id library-items objects page-id component-id shape]
[_ library-id state objects page-id component-id shape]
(let [[all-shapes component root-component]
(resolve-shapes-and-components shape
objects
state
false)]
;; 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
components library-items
reset-touched? false]
(generate-sync-shape-and-children-components root-shape
objects
components
(generate-sync-shape-and-children-components shape
all-shapes
component
root-component
page-id
component-id
reset-touched?)))
false)))
(defn generate-sync-text-shape [shape page-id component-id update-node]
(defn- generate-sync-text-shape [shape page-id component-id update-node]
(let [old-content (:content shape)
new-content (ut/map-node update-node old-content)
rchanges [(d/without-nils {:type :mod-obj
@ -232,59 +239,61 @@
[rchanges lchanges])))
(defmethod generate-sync-shape :colors
[_ library-id library-items _ page-id component-id shape]
[_ library-id state _ 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.
(if (= :text (:type shape))
(let [update-node (fn [node]
(if-let [color (get library-items (:fill-color-ref-id node))]
(assoc node :fill-color (:value color))
node))]
(generate-sync-text-shape shape page-id component-id update-node))
(loop [attrs (seq cp/color-sync-attrs)
roperations []
uoperations []]
(let [attr (first attrs)]
(if (nil? attr)
(if (empty? roperations)
empty-changes
(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)
(recur (next attrs)
roperations
uoperations)
(let [color (get library-items (get shape attr-ref-id))
roperation {:type :set
:attr attr
:val (:value color)
:ignore-touched true}
uoperation {:type :set
:attr attr
:val (get shape attr)
:ignore-touched true}]
(let [colors (get-assets library-id :colors state)]
(if (= :text (:type shape))
(let [update-node (fn [node]
(if-let [color (get colors (:fill-color-ref-id node))]
(assoc node :fill-color (:value color))
node))]
(generate-sync-text-shape shape page-id component-id update-node))
(loop [attrs (seq cp/color-sync-attrs)
roperations []
uoperations []]
(let [attr (first attrs)]
(if (nil? attr)
(if (empty? roperations)
empty-changes
(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)
(recur (next attrs)
(conj roperations roperation)
(conj uoperations uoperation))))))))))
roperations
uoperations)
(let [color (get colors (get shape attr-ref-id))
roperation {:type :set
:attr attr
:val (:value color)
:ignore-touched true}
uoperation {:type :set
:attr attr
:val (get shape attr)
:ignore-touched true}]
(recur (next attrs)
(conj roperations roperation)
(conj uoperations uoperation)))))))))))
(defmethod generate-sync-shape :typographies
[_ library-id library-items _ page-id component-id shape]
[_ library-id state _ 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))]
(let [typographies (get-assets library-id :typographies state)
update-node (fn [node]
(if-let [typography (get typographies (:typography-ref-id node))]
(merge node (d/without-keys typography [:name :id]))
node))]
(generate-sync-text-shape shape page-id component-id update-node)))
@ -292,65 +301,108 @@
;; ---- Component synchronization helpers ----
(defn- get-assets
[library-id asset-type state]
(if (nil? library-id)
(get-in state [:workspace-data asset-type])
(get-in state [:workspace-libraries library-id :data asset-type])))
(defn- get-component
[state file-id component-id]
(let [components (if (nil? file-id)
(get-in state [:workspace-data :components])
(get-in state [:workspace-libraries file-id :data :components]))]
(get components component-id)))
(defn resolve-shapes-and-components
"Get all shapes inside a component instance, and the component they are
linked with. If follow-indirection? is true, and the shape corresponding
to the root shape is also a component instance, follow the link and get
the final component."
[shape objects state follow-indirection?]
(loop [all-shapes (cph/get-object-with-children (:id shape) objects)
local-objects objects
local-shape shape]
(let [root-shape (cph/get-root-shape local-shape local-objects)
component (get-component state
(get root-shape :component-file)
(get root-shape :component-id))
component-shape (get-in component [:objects (:shape-ref local-shape)])]
(if (or (nil? (:component-id component-shape))
(not follow-indirection?))
[all-shapes component component-shape]
(let [resolve-indirection
(fn [shape]
(let [component-shape (get-in component [:objects (:shape-ref shape)])]
(-> shape
(assoc :shape-ref (:shape-ref component-shape))
(d/assoc-when :component-id (:component-id component-shape))
(d/assoc-when :component-file (:component-file component-shape)))))
new-shapes (map resolve-indirection all-shapes)]
(recur new-shapes
(:objects component)
component-shape))))))
(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
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))
root-component (get-in component [:objects (:shape-ref root-shape)])]
(loop [shapes (seq all-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape<-component
shape
root-shape
root-component
component
page-id
component-id
reset-touched?)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
"Generate changes to synchronize one shape that the root of a component
instance, and all its children, from the given component.
If reset? is false, all atributes of each component shape that have
changed, and whose group has not been touched in the instance shape will
be copied to this one.
If reset? is true, all changed attributes will be copied and the 'touched'
flags in the instance shape will be cleared."
[root-shape all-shapes component root-component page-id component-id reset?]
(loop [shapes (seq all-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape<-component
shape
root-shape
root-component
component
page-id
component-id
reset?)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges)))))))
(defn generate-sync-shape-inverse
(defn- generate-sync-shape-inverse
"Generate changes to update the component a shape is linked to, from
the values in the shape and all its children. It acts like the above
function with reset-touched? as true. Also clears the 'touched' flags
in the source shapes."
[root-shape objects components page-id]
(let [all-shapes (cph/get-object-with-children (:id root-shape) objects)
component (get components (:component-id root-shape))
root-component (get-in component [:objects (:shape-ref root-shape)])]
(loop [shapes (seq all-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape->component
shape
root-shape
root-component
component
page-id)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges))))))))
the values in the shape and all its children.
All atributes of each instance shape that have changed, will be copied
to the component shape. Also clears the 'touched' flags in the source
shapes.
And if the component shapes are, in turn, instances of a second component,
their 'touched' flags will be set accordingly."
[root-shape all-shapes component root-component page-id]
(loop [shapes (seq all-shapes)
rchanges []
uchanges []]
(let [shape (first shapes)]
(if (nil? shape)
[rchanges uchanges]
(let [[shape-rchanges shape-uchanges]
(generate-sync-shape->component
shape
root-shape
root-component
component
page-id)]
(recur (next shapes)
(d/concat rchanges shape-rchanges)
(d/concat uchanges shape-uchanges)))))))
(defn generate-sync-shape<-component
(defn- generate-sync-shape<-component
"Generate changes to synchronize one shape that is linked to other shape
inside a component. Same considerations as above about reset-touched?"
[shape root-shape root-component component page-id component-id reset-touched?]
[shape root-shape root-component component page-id component-id reset?]
(if (nil? component)
(remove-component-and-ref shape page-id component-id)
(let [component-shape (get (:objects component) (:shape-ref shape))]
@ -362,25 +414,32 @@
root-component
page-id
component-id
reset-touched?)))))
{:omit-touched? (not reset?)
:reset-touched? reset?
:set-touched? false})))))
(defn generate-sync-shape->component
(defn- generate-sync-shape->component
"Generate changes to synchronize one shape inside a component, with other
shape that is linked to it."
[shape root-shape root-component component page-id]
(js/console.log "component" (clj->js component))
(if (nil? component)
empty-changes
(let [component-shape (get (:objects component) (:shape-ref shape))]
(js/console.log "component-shape" (clj->js component-shape))
(if (nil? component-shape)
empty-changes
(let [[rchanges1 uchanges1]
(let [_(js/console.info "update" (:name shape) "->" (:name component-shape))
[rchanges1 uchanges1]
(update-attrs component-shape
shape
root-component
root-shape
nil
(:id root-component)
true)
{:omit-touched? false
:reset-touched? false
:set-touched? true})
[rchanges2 uchanges2]
(reset-touched shape
page-id
@ -391,13 +450,16 @@
; ---- Operation generation helpers ----
(defn remove-component-and-ref
(defn- remove-component-and-ref
[shape page-id component-id]
[[(d/without-nils {:type :mod-obj
:id (:id shape)
:page-id page-id
:component-id component-id
:operations [{:type :set
:attr :component-root?
:val nil}
{:type :set
:attr :component-id
:val nil}
{:type :set
@ -413,6 +475,9 @@
:page-id page-id
:component-id component-id
:operations [{:type :set
:attr :component-root?
:val (:component-root? shape)}
{:type :set
:attr :component-id
:val (:component-id shape)}
{:type :set
@ -424,7 +489,7 @@
{:type :set-touched
:touched (:touched shape)}]})]])
(defn remove-ref
(defn- -remove-ref
[shape page-id component-id]
[[(d/without-nils {:type :mod-obj
:id (:id shape)
@ -445,7 +510,7 @@
{:type :set-touched
:touched (:touched shape)}]})]])
(defn reset-touched
(defn- reset-touched
[shape page-id component-id]
[[(d/without-nils {:type :mod-obj
:id (:id shape)
@ -460,32 +525,42 @@
:operations [{:type :set-touched
:touched (:touched shape)}]})]])
(defn update-attrs
"The main function that implements the sync algorithm."
[shape component-shape root-shape root-component page-id component-id reset-touched?]
(defn- update-attrs
"The main function that implements the sync algorithm. Copy
attributes that have changed in the origin shape to the dest shape.
If omit-touched? is true, attributes whose group has been touched
in the destination shape will be ignored.
If reset-touched? is true, the 'touched' flags will be cleared in
the dest shape.
If set-touched? is true, the corresponding 'touched' flags will be
set in dest shape if they are different than their current values."
[dest-shape origin-shape dest-root origin-root page-id component-id
{:keys [omit-touched? reset-touched? set-touched?] :as options}]
;; === Uncomment this to debug synchronization ===
;; (println "SYNC"
;; "[C]" (:name component-shape)
;; "[C]" (:name origin-shape)
;; "->"
;; (if page-id "[W]" ["C"])
;; (:name shape))
;; (:name dest-shape)
;; (str options))
(let [; The position attributes need a special sync algorith, because we do
; not synchronize the absolute position, but the position relative of
; the container shape of the component.
new-pos (calc-new-pos shape component-shape root-shape root-component)
pos-group (get cp/component-sync-attrs :x)
touched (get shape :touched #{})]
new-pos (calc-new-pos dest-shape origin-shape dest-root origin-root)
touched (get dest-shape :touched #{})]
(loop [attrs (seq (keys (dissoc cp/component-sync-attrs :x :y)))
roperations (if (or (not (touched pos-group)) reset-touched? true)
[{:type :set :attr :x :val (:x new-pos)} ; ^ TODO: the position-group is being set
{:type :set :attr :y :val (:y new-pos)}] ; | as touched somewhere. Investigate why.
roperations (if (or (not= (:x new-pos) (:x dest-shape))
(not= (:y new-pos) (:y dest-shape)))
[{:type :set :attr :x :val (:x new-pos)}
{:type :set :attr :y :val (:y new-pos)}]
[])
uoperations (if (or (not (touched pos-group)) reset-touched? true)
[{:type :set :attr :x :val (:x shape)}
{:type :set :attr :y :val (:y shape)}]
uoperations (if (or (not= (:x new-pos) (:x dest-shape))
(not= (:y new-pos) (:y dest-shape)))
[{:type :set :attr :x :val (:x dest-shape)}
{:type :set :attr :y :val (:y dest-shape)}]
[])]
(let [attr (first attrs)]
@ -499,51 +574,50 @@
uoperations (if reset-touched?
(conj uoperations
{:type :set-touched
:touched (:touched shape)})
:touched (:touched dest-shape)})
uoperations)
rchanges [(d/without-nils {:type :mod-obj
:id (:id shape)
:id (:id dest-shape)
:page-id page-id
:component-id component-id
:operations roperations})]
uchanges [(d/without-nils {:type :mod-obj
:id (:id shape)
:id (:id dest-shape)
:page-id page-id
:component-id component-id
:operations uoperations})]]
[rchanges uchanges])
(if-not (contains? shape attr)
(if-not (contains? dest-shape attr)
(recur (next attrs)
roperations
uoperations)
(let [roperation {:type :set
:attr attr
:val (get component-shape attr)
:ignore-touched true}
:val (get origin-shape attr)
:ignore-touched (not set-touched?)}
uoperation {:type :set
:attr attr
:val (get shape attr)
:ignore-touched true}
:val (get dest-shape attr)
:ignore-touched (not set-touched?)}
attr-group (get cp/component-sync-attrs attr)]
(if (or (not (touched attr-group)) reset-touched?)
(recur (next attrs)
(conj roperations roperation)
(conj uoperations uoperation))
(if (and (touched attr-group) omit-touched?)
(recur (next attrs)
roperations
uoperations)))))))))
uoperations)
(recur (next attrs)
(conj roperations roperation)
(conj uoperations uoperation))))))))))
(defn calc-new-pos
[shape component-shape root-shape root-component]
(let [root-pos (gpt/point (:x root-shape) (:y root-shape))
root-component-pos (gpt/point (:x root-component) (:y root-component))
component-pos (gpt/point (:x component-shape) (:y component-shape))
delta (gpt/subtract component-pos root-component-pos)
shape-pos (gpt/point (:x shape) (:y shape))
new-pos (gpt/add root-pos delta)]
(defn- calc-new-pos
[dest-shape origin-shape dest-root origin-root]
(let [root-pos (gpt/point (:x dest-root) (:y dest-root))
origin-root-pos (gpt/point (:x origin-root) (:y origin-root))
origin-pos (gpt/point (:x origin-shape) (:y origin-shape))
delta (gpt/subtract origin-pos origin-root-pos)
shape-pos (gpt/point (:x dest-shape) (:y dest-shape))
new-pos (gpt/add root-pos delta)]
new-pos))

View file

@ -106,8 +106,7 @@
(show-component [shape objects]
(if (nil? (:shape-ref shape))
""
(let [root-id (cph/get-root-component (:id shape) objects)
root-shape (when root-id (get objects root-id))
(let [root-shape (cph/get-root-shape shape objects)
component-id (when root-shape (:component-id root-shape))
component-file-id (when root-shape (:component-file root-shape))
component-file (when component-file-id (get libraries component-file-id))
@ -118,7 +117,9 @@
component-shape (when (and component (:shape-ref shape))
(get-in component [:objects (:shape-ref shape)]))]
(str/format " %s--> %s%s%s"
(if (:component-root? shape) "#" "-")
(cond (:component-root? shape) "#"
(:component-id shape) "@"
:else "-")
(when component-file (str/format "<%s> " (:name component-file)))
(:name component-shape)
(if (or (:component-root? shape)

View file

@ -20,6 +20,7 @@
[app.main.ui.icons :as i]
[app.util.dom :as dom]
[app.main.data.workspace :as dw]
[app.main.data.workspace.common :as dwc]
[app.main.data.workspace.libraries :as dwl]
[app.main.ui.hooks :refer [use-rxsub]]
[app.main.ui.components.dropdown :refer [dropdown]]))
@ -65,8 +66,10 @@
do-detach-component #(st/emit! (dwl/detach-component id))
do-reset-component #(st/emit! (dwl/reset-component id))
do-update-component #(do
(st/emit! dwc/start-undo-transaction)
(st/emit! (dwl/update-component id))
(st/emit! (dwl/sync-file nil)))
(st/emit! (dwl/sync-file nil))
(st/emit! dwc/commit-undo-transaction))
do-navigate-component-file #(st/emit! (dwl/nav-to-component-file
(:component-file shape)))]
[:*

View file

@ -89,7 +89,8 @@
:default-value (:name shape "")}]
[:span.element-name
{:on-double-click on-click}
(:name shape "")])))
(:name shape "")
(when (seq (:touched shape)) " *")])))
(defn- make-collapsed-iref
[id]
@ -305,6 +306,7 @@
:component-id
:component-file
:shape-ref
:touched
:metadata])]
(persistent!
(reduce-kv (fn [res id obj]