🐛 Fixes issues with moving shapes outside groups

This commit is contained in:
alonso.torres 2021-02-09 14:41:58 +01:00 committed by Andrey Antukh
parent c1a139fc51
commit 4d5231598f
4 changed files with 257 additions and 166 deletions

View file

@ -360,7 +360,7 @@
(t/is (= [rect-a-id rect-e-id rect-d-id] (t/is (= [rect-a-id rect-e-id rect-d-id]
(get-in objects [group-b-id :shapes])))))) (get-in objects [group-b-id :shapes]))))))
(t/testing "Move elements and delete the empty group" (t/testing "Move all elements from a group"
(let [changes [{:type :mov-objects (let [changes [{:type :mov-objects
:page-id page-id :page-id page-id
:parent-id group-a-id :parent-id group-a-id
@ -368,9 +368,9 @@
res (cp/process-changes data changes)] res (cp/process-changes data changes)]
(let [objects (get-in res [:pages-index page-id :objects])] (let [objects (get-in res [:pages-index page-id :objects])]
(t/is (= [group-a-id rect-e-id] (t/is (= [group-a-id group-b-id rect-e-id]
(get-in objects [frame-a-id :shapes]))) (get-in objects [frame-a-id :shapes])))
(t/is (nil? (get-in objects [group-b-id])))))) (t/is (empty? (get-in objects [group-b-id :shapes]))))))
(t/testing "Move elements to a group with different frame" (t/testing "Move elements to a group with different frame"
(let [changes [{:type :mov-objects (let [changes [{:type :mov-objects
@ -727,11 +727,11 @@
;; After ;; After
(t/is (= [shape-2-id shape-1-id shape-3-id shape-4-id] (t/is (= [shape-2-id shape-1-id shape-3-id shape-4-id group-1-id]
(get-in res [:pages-index page-id :objects cp/root :shapes]))) (get-in res [:pages-index page-id :objects cp/root :shapes])))
(t/is (= nil (t/is (not= nil
(get-in res [:pages-index page-id :objects group-1-id]))) (get-in res [:pages-index page-id :objects group-1-id])))
)) ))

View file

@ -36,8 +36,20 @@
(when verify? (when verify?
(us/verify ::spec/changes items)) (us/verify ::spec/changes items))
(->> items (let [pages (into #{} (map :page-id) items)
(reduce #(or (process-change %1 %2) %1) data)))) result (->> items
(reduce #(or (process-change %1 %2) %1) data))]
;; Validate result shapes (only on the backend)
#?(:clj
(doseq [page-id pages]
(let [page (get-in result [:pages-index page-id])]
(doseq [[id shape] (:objects page)]
(if-not (= shape (get-in data [:pages-index page-id :objects id]))
;; If object has change verify is correct
(us/verify ::spec/shape shape))))))
result)))
(defmethod process-change :set-option (defmethod process-change :set-option
[data {:keys [page-id option value]}] [data {:keys [page-id option value]}]
@ -94,7 +106,6 @@
(let [update-fn (fn [objects] (let [update-fn (fn [objects]
(if-let [obj (get objects id)] (if-let [obj (get objects id)]
(let [result (reduce process-operation obj operations)] (let [result (reduce process-operation obj operations)]
#?(:clj (us/verify ::spec/shape result))
(assoc objects id result)) (assoc objects id result))
objects))] objects))]
(if page-id (if page-id
@ -142,16 +153,25 @@
(map :id) (map :id)
(distinct)) (distinct))
shapes))) shapes)))
(set-mask-selrect [group children]
(let [mask (first children)]
(-> group
(merge (select-keys mask [:selrect :points]))
(assoc :x (-> mask :selrect :x)
:y (-> mask :selrect :y)
:width (-> mask :selrect :width)
:height (-> mask :selrect :height)))))
(update-group [group objects] (update-group [group objects]
(let [children (->> group :shapes (map #(get objects %)))] (let [children (->> group :shapes (map #(get objects %)))]
(if (:masked-group? group) (cond
(let [mask (first children)] ;; If the group is empty we don't make any changes. Should be removed by a later process
(-> group (empty? children)
(merge (select-keys mask [:selrect :points])) group
(assoc :x (-> mask :selrect :x)
:y (-> mask :selrect :y) (:masked-group? group)
:width (-> mask :selrect :width) (set-mask-selrect group children)
:height (-> mask :selrect :height))))
:else
(gsh/update-group-selrect group children))))] (gsh/update-group-selrect group children))))]
(if page-id (if page-id
@ -206,23 +226,17 @@
pid prev-parent-id pid prev-parent-id
objects objects] objects objects]
(let [obj (get objects pid)] (let [obj (get objects pid)]
(if (and (= 1 (count (:shapes obj))) (cond-> objects
(= sid (first (:shapes obj))) true
(= :group (:type obj))) (update-in [pid :shapes] strip-id sid)
(recur pid
(:parent-id obj)
(dissoc objects pid))
(cond-> objects
true
(update-in [pid :shapes] strip-id sid)
(and (:shape-ref obj) (and (:shape-ref obj)
(= (:type obj) :group) (= (:type obj) :group)
(not ignore-touched)) (not ignore-touched))
(-> (->
(update-in [pid :touched] (update-in [pid :touched]
cph/set-touched-group :shapes-group) cph/set-touched-group :shapes-group)
(d/dissoc-in [pid :remote-synced?]))))))))) (d/dissoc-in [pid :remote-synced?]))))))))
(update-parent-id [objects id] (update-parent-id [objects id]
(assoc-in objects [id :parent-id] parent-id)) (assoc-in objects [id :parent-id] parent-id))

View file

@ -808,6 +808,168 @@
;; --- Change Shape Order (D&D Ordering) ;; --- Change Shape Order (D&D Ordering)
(defn relocate-shapes-changes [objects parents parent-id page-id to-index ids groups-to-delete groups-to-unmask shapes-to-detach shapes-to-reroot shapes-to-deroot]
(let [;; Changes to the shapes that are being move
r-mov-change
[{:type :mov-objects
:parent-id parent-id
:page-id page-id
:index to-index
:shapes (vec (reverse ids))}]
u-mov-change
(map (fn [id]
(let [obj (get objects id)]
{:type :mov-objects
:parent-id (:parent-id obj)
:page-id page-id
:index (cp/position-on-parent id objects)
:shapes [id]}))
(reverse ids))
;; Changes deleting empty groups
r-del-change
(map (fn [group-id]
{:type :del-obj
:page-id page-id
:id group-id})
groups-to-delete)
u-del-change
(d/concat
[]
;; Create the groups
(map (fn [group-id]
(let [group (get objects group-id)]
{:type :add-obj
:page-id page-id
:parent-id parent-id
:frame-id (:frame-id group)
:id group-id
:obj (-> group
(assoc :shapes []))}))
groups-to-delete)
;; Creates the hierarchy
(map (fn [group-id]
(let [group (get objects group-id)]
{:type :mov-objects
:page-id page-id
:parent-id (:id group)
:shapes (:shapes group)}))
groups-to-delete))
;; Changes removing the masks from the groups without mask shape
r-mask-change
(map (fn [group-id]
{:type :mod-obj
:page-id page-id
:id group-id
:operations [{:type :set
:attr :masked-group?
:val false}]})
groups-to-unmask)
u-mask-change
(map (fn [group-id]
(let [group (get objects group-id)]
{:type :mod-obj
:page-id page-id
:id group-id
:operations [{:type :set
:attr :masked-group?
:val (:masked-group? group)}]}))
groups-to-unmask)
;; Changes to the components metadata
detach-keys [:component-id :component-file :component-root? :remote-synced? :shape-ref :touched]
r-detach-change
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations (mapv #(hash-map :type :set :attr % :val nil) detach-keys)})
shapes-to-detach)
u-detach-change
(map (fn [id]
(let [obj (get objects id)]
{:type :mod-obj
:page-id page-id
:id id
:operations (mapv #(hash-map :type :set :attr % :val (get obj %)) detach-keys)}))
shapes-to-detach)
r-deroot-change
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val nil}]})
shapes-to-deroot)
u-deroot-change
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val true}]})
shapes-to-deroot)
r-reroot-change
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val true}]})
shapes-to-reroot)
u-reroot-change
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val nil}]})
shapes-to-reroot)
r-reg-change
[{:type :reg-objects
:page-id page-id
:shapes (vec parents)}]
u-reg-change
[{:type :reg-objects
:page-id page-id
:shapes (vec parents)}]
rchanges (d/concat []
r-mov-change
r-del-change
r-mask-change
r-detach-change
r-deroot-change
r-reroot-change
r-reg-change)
uchanges (d/concat []
u-del-change
u-reroot-change
u-deroot-change
u-detach-change
u-mask-change
u-mov-change
u-reg-change)]
[rchanges uchanges]))
(defn relocate-shapes (defn relocate-shapes
[ids parent-id to-index] [ids parent-id to-index]
(us/verify (s/coll-of ::us/uuid) ids) (us/verify (s/coll-of ::us/uuid) ids)
@ -826,13 +988,37 @@
;; If we try to move a parent into a child we remove it ;; If we try to move a parent into a child we remove it
ids (filter #(not (cp/is-parent? objects parent-id %)) ids) ids (filter #(not (cp/is-parent? objects parent-id %)) ids)
parents (loop [res #{parent-id} parents (reduce (fn [result id]
ids (seq ids)] (conj result (cp/get-parent id objects)))
(if (nil? ids) #{parent-id} ids)
(vec res)
(recur groups-to-delete
(conj res (cp/get-parent (first ids) objects)) (loop [current-id (first parents)
(next ids)))) to-check (rest parents)
removed-id? (set ids)
result #{}]
(if-not current-id
;; Base case, no next element
result
(let [group (get objects current-id)]
(if (and (not= uuid/zero current-id)
(not= current-id parent-id)
(empty? (remove removed-id? (:shapes group))))
;; Adds group to the remove and check its parent
(let [to-check (d/concat [] to-check [(cp/get-parent current-id objects)]) ]
(recur (first to-check)
(rest to-check)
(conj removed-id? current-id)
(conj result current-id)))
;; otherwise recur
(recur (first to-check)
(rest to-check)
removed-id?
result)))))
groups-to-unmask groups-to-unmask
(reduce (fn [group-ids id] (reduce (fn [group-ids id]
@ -849,6 +1035,10 @@
#{} #{}
ids) ids)
;; Sets the correct components metadata for the moved shapes
;; `shapes-to-detach` Detach from a component instance a shape that was inside a component and is moved outside
;; `shapes-to-deroot` Removes the root flag from a component instance moved inside another component
;; `shapes-to-reroot` Adds a root flag when a nested component instance is moved outside
[shapes-to-detach shapes-to-deroot shapes-to-reroot] [shapes-to-detach shapes-to-deroot shapes-to-reroot]
(reduce (fn [[shapes-to-detach shapes-to-deroot shapes-to-reroot] id] (reduce (fn [[shapes-to-detach shapes-to-deroot shapes-to-reroot] id]
(let [shape (get objects id) (let [shape (get objects id)
@ -876,131 +1066,18 @@
[[] [] []] [[] [] []]
ids) ids)
rchanges (d/concat [rchanges uchanges] (relocate-shapes-changes objects
[{:type :mov-objects parents
:parent-id parent-id parent-id
:page-id page-id page-id
:index to-index to-index
:shapes (vec (reverse ids))} ids
{:type :reg-objects groups-to-delete
:page-id page-id groups-to-unmask
:shapes parents}] shapes-to-detach
(map (fn [group-id] shapes-to-reroot
{:type :mod-obj shapes-to-deroot)]
:page-id page-id (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
:id group-id
:operations [{:type :set
:attr :masked-group?
:val false}]})
groups-to-unmask)
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-id
:val nil}
{:type :set
:attr :component-file
:val nil}
{:type :set
:attr :component-root?
:val nil}
{:type :set
:attr :remote-synced?
:val nil}
{:type :set
:attr :shape-ref
:val nil}
{:type :set
:attr :touched
:val nil}]})
shapes-to-detach)
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val nil}]})
shapes-to-deroot)
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val true}]})
shapes-to-reroot))
uchanges (d/concat
(reduce (fn [res id]
(let [obj (get objects id)]
(conj res
{:type :mov-objects
:parent-id (:parent-id obj)
:page-id page-id
:index (cp/position-on-parent id objects)
:shapes [id]})))
[] (reverse ids))
[{:type :reg-objects
:page-id page-id
:shapes parents}]
(map (fn [group-id]
{:type :mod-obj
:page-id page-id
:id group-id
:operations [{:type :set
:attr :masked-group?
:val true}]})
groups-to-unmask)
(map (fn [id]
(let [obj (get objects id)]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-id
:val (:component-id obj)}
{:type :set
:attr :component-file
:val (:component-file obj)}
{:type :set
:attr :component-root?
:val (:component-root? obj)}
{:type :set
:attr :remote-synced?
:val (:remote-synced? obj)}
{:type :set
:attr :shape-ref
:val (:shape-ref obj)}
{:type :set
:attr :touched
:val (:touched obj)}]}))
shapes-to-detach)
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val true}]})
shapes-to-deroot)
(map (fn [id]
{:type :mod-obj
:page-id page-id
:id id
:operations [{:type :set
:attr :component-root?
:val nil}]})
shapes-to-reroot))]
;; (println "================ rchanges")
;; (cljs.pprint/pprint rchanges)
;; (println "================ uchanges")
;; (cljs.pprint/pprint uchanges)
(rx/of (dwc/commit-changes rchanges uchanges
{:commit-local? true})
(dwc/expand-collapse parent-id)))))) (dwc/expand-collapse parent-id))))))
(defn relocate-selected-shapes (defn relocate-selected-shapes

View file

@ -198,7 +198,7 @@
(defn retrieve-used-names (defn retrieve-used-names
[objects] [objects]
(into #{} (map :name) (vals objects))) (into #{} (comp (map :name) (remove nil?)) (vals objects)))
(defn generate-unique-name (defn generate-unique-name