diff --git a/backend/tests/uxbox/tests/test_common_pages.clj b/backend/tests/uxbox/tests/test_common_pages.clj index 5f48b90d9e..e30eaa52fd 100644 --- a/backend/tests/uxbox/tests/test_common_pages.clj +++ b/backend/tests/uxbox/tests/test_common_pages.clj @@ -469,39 +469,88 @@ )) -(t/deftest process-change-move-objects-regression - (let [shape-2-id (uuid/custom 1 2) - shape-3-id (uuid/custom 1 3) +(t/deftest process-change-mov-objects-regression + (let [shape-1-id (uuid/custom 2 1) + shape-2-id (uuid/custom 2 2) + shape-3-id (uuid/custom 2 3) frame-id (uuid/custom 1 1) changes [{:type :add-obj :id frame-id + :parent-id uuid/zero :frame-id uuid/zero :obj {:type :frame :name "Frame"}} {:type :add-obj :frame-id frame-id - :id shape-2-id + :parent-id frame-id + :id shape-1-id :obj {:type :shape - :name "Shape"}} + :name "Shape 1"}} {:type :add-obj - :id shape-3-id + :id shape-2-id + :parent-id uuid/zero :frame-id uuid/zero :obj {:type :rect - :name "Shape"}}] + :name "Shape 2"}} + + {:type :add-obj + :id shape-3-id + :parent-id uuid/zero + :frame-id uuid/zero + :obj {:type :rect + :name "Shape 3"}} + ] data (cp/process-changes cp/default-page-data changes)] + + (t/testing "preserve order on multiple shape mov 1" + (let [changes [{:type :mov-objects + :shapes [shape-2-id shape-3-id] + :parent-id uuid/zero + :index 0}] + res (cp/process-changes data changes)] + + ;; (println "==> BEFORE") + ;; (pprint (get-in data [:objects])) + ;; (println "==> AFTER") + ;; (pprint (get-in res [:objects])) + + (t/is (= [frame-id shape-2-id shape-3-id] + (get-in data [:objects uuid/zero :shapes]))) + (t/is (= [shape-2-id shape-3-id frame-id] + (get-in res [:objects uuid/zero :shapes]))))) + + (t/testing "preserve order on multiple shape mov 1" + (let [changes [{:type :mov-objects + :shapes [shape-3-id shape-2-id] + :parent-id uuid/zero + :index 0}] + res (cp/process-changes data changes)] + + ;; (println "==> BEFORE") + ;; (pprint (get-in data [:objects])) + ;; (println "==> AFTER") + ;; (pprint (get-in res [:objects])) + + (t/is (= [frame-id shape-2-id shape-3-id] + (get-in data [:objects uuid/zero :shapes]))) + (t/is (= [shape-3-id shape-2-id frame-id] + (get-in res [:objects uuid/zero :shapes]))))) + (t/testing "move inside->outside-inside" (let [changes [{:type :mov-objects - :shapes [shape-3-id] + :shapes [shape-2-id] :parent-id frame-id} {:type :mov-objects - :shapes [shape-3-id] + :shapes [shape-2-id] :parent-id uuid/zero}] res (cp/process-changes data changes)] + (t/is (= (get-in res [:objects shape-1-id :frame-id]) + (get-in data [:objects shape-1-id :frame-id]))) (t/is (= (get-in res [:objects shape-2-id :frame-id]) - (get-in data [:objects shape-2-id :frame-id]))) - (t/is (= (get-in res [:objects shape-3-id :frame-id]) - (get-in data [:objects shape-3-id :frame-id]))))))) + (get-in data [:objects shape-2-id :frame-id]))))) + + )) (t/deftest process-change-move-objects-2 @@ -762,6 +811,3 @@ (t/is (= [#uuid "3375ec40-ab24-11ea-b512-b945e8edccf5"] (get-in res1 [:objects uuid/zero :shapes]))) )) - - - diff --git a/common/uxbox/common/data.cljc b/common/uxbox/common/data.cljc index 401e1818b7..77236bbeb0 100644 --- a/common/uxbox/common/data.cljc +++ b/common/uxbox/common/data.cljc @@ -48,10 +48,10 @@ (defn concat [& colls] (loop [result (transient (first colls)) - colls (rest colls)] - (if (seq colls) + colls (next colls)] + (if colls (recur (reduce conj! result (first colls)) - (rest colls)) + (next colls)) (persistent! result)))) (defn enumerate diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index 5ed758798c..07e05a907e 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -184,19 +184,16 @@ (defmethod change-spec-impl :add-obj [_] (s/keys :req-un [::id ::frame-id ::obj] - :opt-un [::session-id ::parent-id])) + :opt-un [::parent-id])) (defmethod change-spec-impl :mod-obj [_] - (s/keys :req-un [::id ::operations] - :opt-un [::session-id])) + (s/keys :req-un [::id ::operations])) (defmethod change-spec-impl :del-obj [_] - (s/keys :req-un [::id] - :opt-un [::session-id])) + (s/keys :req-un [::id])) -(defmethod change-spec-impl :reg-obj [_] - (s/keys :req-un [::ids] - :opt-un [::session-id])) +(defmethod change-spec-impl :reg-objects [_] + (s/keys :req-un [::shapes])) (defmethod change-spec-impl :mov-objects [_] (s/keys :req-un [::parent-id ::shapes] @@ -356,15 +353,15 @@ (seq shapes) ; Recursive delete all dependend objects (as-> $ (reduce #(or (process-change %1 {:type :del-obj :id %2}) %1) $ shapes)))))) -(defmethod process-change :reg-obj - [data {:keys [ids]}] +(defmethod process-change :reg-objects + [data {:keys [shapes]}] (let [objects (:objects data)] - (loop [ids ids data data] - (if (seq ids) - (let [item (get objects (first ids))] + (loop [shapes shapes data data] + (if (seq shapes) + (let [item (get objects (first shapes))] (if (= :group (:type item)) (recur - (rest ids) + (rest shapes) (update-in data [:objects (:id item)] (fn [{:keys [shapes] :as obj}] (let [shapes (->> shapes @@ -381,7 +378,7 @@ (assoc $ :points (geom/shape->points $)) (assoc $ :selrect (geom/points->selrect (:points $))))) obj))))) - (recur (rest ids) data))) + (recur (rest shapes) data))) data)))) (defmethod process-change :mov-objects diff --git a/common/uxbox/common/pages_helpers.cljc b/common/uxbox/common/pages_helpers.cljc index 81e21cfd2d..2516c274ee 100644 --- a/common/uxbox/common/pages_helpers.cljc +++ b/common/uxbox/common/pages_helpers.cljc @@ -39,6 +39,15 @@ (when parent-id (lazy-seq (cons parent-id (get-parents parent-id objects)))))) +(defn get-common-parents + [ids objects] + (loop [res (d/ordered-set) + ids (seq ids)] + (if ids + (recur (into res (get-parents (first ids) objects)) + (next ids)) + res))) + (defn generate-child-parent-index [objects] (reduce-kv diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index c2beee199a..f0932dee08 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -659,7 +659,6 @@ objects (get-in state [:workspace-data page-id :objects]) del-change #(array-map :type :del-obj :id %) - reg-change #(array-map :type :reg-obj :id %) get-empty-parents (fn get-empty-parents [parents] @@ -680,7 +679,7 @@ (map del-change (reverse children)) [(del-change id)] (map del-change (get-empty-parents parents)) - [{:type :reg-obj :ids parents}]))) + [{:type :reg-objects :shapes (vec parents)}]))) [] ids) @@ -700,7 +699,7 @@ (map add-chg (reverse (get-empty-parents parents))) [(add-chg id)] (map add-chg children) - [{:type :reg-obj :ids parents}]))) + [{:type :reg-objects :shapes (vec parents)}]))) [] ids) ] @@ -772,33 +771,51 @@ ;; --- Change Shape Order (D&D Ordering) -(defn relocate-shape - [id parent-id to-index] - (us/verify ::us/uuid id) +(defn relocate-shapes + [ids parent-id to-index] + (us/verify (s/coll-of ::us/uuid) ids) (us/verify ::us/uuid parent-id) (us/verify number? to-index) (ptk/reify ::relocate-shape - dwc/IUpdateGroup - (get-ids [_] [id]) - ptk/WatchEvent (watch [_ state stream] - (let [page-id (:current-page-id state) - objects (get-in state [:workspace-data page-id :objects]) - parent (get objects (cph/get-parent id objects)) - current-index (d/index-of (:shapes parent) id) - selected (get-in state [:workspace-local :selected])] - (rx/of (dwc/commit-changes [{:type :mov-objects - :parent-id parent-id - :index to-index - :shapes (vec selected)}] - [{:type :mov-objects - :parent-id (:id parent) - :index current-index - :shapes (vec selected)}] + (let [page-id (:current-page-id state) + objects (get-in state [:workspace-data page-id :objects]) + parents (cph/get-common-parents ids objects) + + rchanges [{:type :mov-objects + :parent-id parent-id + :index to-index + :shapes (vec (reverse ids))} + {:type :reg-objects + :shapes (vec (conj parents parent-id))}] + + uchanges + (reduce (fn [res id] + (let [obj (get objects id)] + (conj res + {:type :mov-objects + :parent-id (:parent-id obj) + :index (cph/position-on-parent id objects) + :shapes [id]}))) + [] (reverse ids)) + uchanges (conj uchanges + {:type :reg-objects + :shapes (vec parents)})] + + (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) +(defn relocate-selected-shapes + [parent-id to-index] + (ptk/reify ::relocate-selected-shapes + ptk/WatchEvent + (watch [_ state stream] + (let [selected (get-in state [:workspace-local :selected])] + (rx/of (relocate-shapes selected parent-id to-index)))))) + + ;; --- Change Page Order (D&D Ordering) (defn relocate-page diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs index 5275fa4898..0589ed1cd7 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs @@ -163,10 +163,10 @@ on-drop (fn [side {:keys [id] :as data}] (if (= side :center) - (st/emit! (dw/relocate-shape id (:id item) 0)) + (st/emit! (dw/relocate-selected-shapes (:id item) 0)) (let [to-index (if (= side :top) (inc index) index) parent-id (cph/get-parent (:id item) objects)] - (st/emit! (dw/relocate-shape id parent-id to-index))))) + (st/emit! (dw/relocate-selected-shapes parent-id to-index))))) on-hold (fn []