diff --git a/common/app/common/pages.cljc b/common/app/common/pages.cljc index 9ba3cbd0b..81791d652 100644 --- a/common/app/common/pages.cljc +++ b/common/app/common/pages.cljc @@ -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] diff --git a/common/app/common/pages_helpers.cljc b/common/app/common/pages_helpers.cljc index f915ad9ee..5edbfd70c 100644 --- a/common/app/common/pages_helpers.cljc +++ b/common/app/common/pages_helpers.cljc @@ -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" diff --git a/frontend/src/app/main/data/workspace.cljs b/frontend/src/app/main/data/workspace.cljs index 7349ea59a..5fcd337cd 100644 --- a/frontend/src/app/main/data/workspace.cljs +++ b/frontend/src/app/main/data/workspace.cljs @@ -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 diff --git a/frontend/src/app/main/data/workspace/libraries.cljs b/frontend/src/app/main/data/workspace/libraries.cljs index 8fb693997..15a0e254d 100644 --- a/frontend/src/app/main/data/workspace/libraries.cljs +++ b/frontend/src/app/main/data/workspace/libraries.cljs @@ -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 diff --git a/frontend/src/app/main/data/workspace/libraries_helpers.cljs b/frontend/src/app/main/data/workspace/libraries_helpers.cljs index e5e6835a2..e9a89be2b 100644 --- a/frontend/src/app/main/data/workspace/libraries_helpers.cljs +++ b/frontend/src/app/main/data/workspace/libraries_helpers.cljs @@ -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)) - diff --git a/frontend/src/app/main/store.cljs b/frontend/src/app/main/store.cljs index b3afec596..9e34aa323 100644 --- a/frontend/src/app/main/store.cljs +++ b/frontend/src/app/main/store.cljs @@ -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) diff --git a/frontend/src/app/main/ui/workspace/context_menu.cljs b/frontend/src/app/main/ui/workspace/context_menu.cljs index a7fea323b..cc109cc50 100644 --- a/frontend/src/app/main/ui/workspace/context_menu.cljs +++ b/frontend/src/app/main/ui/workspace/context_menu.cljs @@ -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)))] [:* diff --git a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs index 42b89610c..889d04dcd 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/layers.cljs @@ -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]