diff --git a/src/uxbox/data/workspace.cljs b/src/uxbox/data/workspace.cljs index bc7bc085c..926d979d8 100644 --- a/src/uxbox/data/workspace.cljs +++ b/src/uxbox/data/workspace.cljs @@ -385,22 +385,11 @@ (defn duplicate-selected [] - (letfn [(all-toplevel? [coll] - (every? #(nil? (:parent %)) coll))] - (reify - rs/UpdateEvent - (-apply-update [_ state] - (let [selected (->> (get-in state [:workspace :selected]) - (mapv #(get-in state [:shapes-by-id %]))) - page (get-in state [:workspace :page])] - (cond - (all-toplevel? selected) - (reduce #(stsh/assoc-shape-to-page %1 %2 page) state selected) - - :else - (do - (println "Not implemented") - state))))))) + (reify + rs/UpdateEvent + (-apply-update [_ state] + (let [selected (get-in state [:workspace :selected])] + (stsh/duplicate-shapes state selected))))) (defn delete-selected "Deselect all and remove all selected shapes." diff --git a/src/uxbox/state/shapes.cljs b/src/uxbox/state/shapes.cljs index 96244214e..28c018cbd 100644 --- a/src/uxbox/state/shapes.cljs +++ b/src/uxbox/state/shapes.cljs @@ -3,7 +3,6 @@ (:require [uxbox.shapes :as sh] [uxbox.util.data :refer (index-of)])) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shape Creation ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -16,6 +15,60 @@ (update-in $ [:pages-by-id page :shapes] conj sid) (assoc-in $ [:shapes-by-id sid] shape)))) +(defn duplicate-shapes' + ([state shapes page] + (duplicate-shapes' state shapes page nil)) + ([state shapes page group] + (letfn [(duplicate-shape [state shape page group] + (if (= (:type shape) :builtin/group) + (let [id (random-uuid) + items (:items shape) + shape (assoc shape :id id :page page :items []) + state (if (nil? group) + (as-> state $ + (update-in $ [:pages-by-id page :shapes] conj id) + (assoc-in $ [:shapes-by-id id] shape)) + (as-> state $ + (update-in $ [:shapes-by-id group :items] conj id) + (assoc-in $ [:shapes-by-id id] shape)))] + (->> (map #(get-in state [:shapes-by-id %]) items) + (reduce #(duplicate-shape %1 %2 page id) state))) + (let [id (random-uuid) + shape (-> (dissoc shape :group) + (assoc :id id :page page) + (merge (when group {:group group})))] + (if (nil? group) + (as-> state $ + (update-in $ [:pages-by-id page :shapes] conj id) + (assoc-in $ [:shapes-by-id id] shape)) + (as-> state $ + (update-in $ [:shapes-by-id group :items] conj id) + (assoc-in $ [:shapes-by-id id] shape))))))] + + (reduce #(duplicate-shape %1 %2 page group) state shapes)))) + +(defn duplicate-shapes + [state shapes] + (letfn [(all-toplevel? [coll] + (every? #(nil? (:group %)) coll)) + (all-same-group? [coll] + (let [group (:group (first coll))] + (every? #(= group (:group %)) coll)))] + (let [shapes (mapv #(get-in state [:shapes-by-id %]) shapes)] + (cond + (all-toplevel? shapes) + (let [page (:page (first shapes))] + (duplicate-shapes' state shapes page)) + + (all-same-group? shapes) + (let [page (:page (first shapes)) + group (:group (first shapes))] + (duplicate-shapes' state shapes page group)) + + :else + (let [page (:page (first shapes))] + (duplicate-shapes' state shapes page)))))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Delete Shapes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/test/uxbox/data/workspace_tests.cljs b/test/uxbox/data/workspace_tests.cljs index 5eb377987..4cf4dd040 100644 --- a/test/uxbox/data/workspace_tests.cljs +++ b/test/uxbox/data/workspace_tests.cljs @@ -168,5 +168,3 @@ ;; (pprint result) (t/is (= result expected)) (t/is (vector? (get-in result [:pages-by-id 1 :shapes]))))) - - diff --git a/test/uxbox/state/shapes_tests.cljs b/test/uxbox/state/shapes_tests.cljs new file mode 100644 index 000000000..f60424db9 --- /dev/null +++ b/test/uxbox/state/shapes_tests.cljs @@ -0,0 +1,93 @@ +(ns uxbox.state.shapes-tests + (:require [cljs.test :as t :include-macros true] + [cljs.pprint :refer (pprint)] + [uxbox.state.shapes :as ssh])) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Duplicate (one shape) +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(defn constantly-inc + [init] + (let [v (atom init)] + (fn [& args] + (let [result @v] + (swap! v inc) + result)))) + +;; duplicate shape: duplicate simple shape +(t/deftest duplicate-shapes-test1 + (let [initial {:pages-by-id {1 {:id 1 :shapes [1]}} + :shapes-by-id {1 {:id 1 :page 1}}} + + expected (-> initial + (assoc-in [:pages-by-id 1 :shapes] [1 2]) + (assoc-in [:shapes-by-id 2] {:id 2 :page 1}))] + + (with-redefs [cljs.core/random-uuid (constantly 2)] + (let [result (ssh/duplicate-shapes initial [1])] + ;; (pprint expected) + ;; (pprint result) + (t/is (= result expected)))))) + + +;; duplicate shape: duplicate inside group +(t/deftest duplicate-shapes-test2 + (let [initial {:pages-by-id {1 {:id 1 :shapes [1]}} + :shapes-by-id {1 {:id 1 :page 1 + :type :builtin/group + :items [2 3]} + 2 {:id 2 :page 1 :group 1} + 3 {:id 3 :page 1 :group 1}}} + + expected (-> initial + (assoc-in [:shapes-by-id 1 :items] [2 3 4 5]) + (assoc-in [:shapes-by-id 4] {:id 4 :page 1 :group 1}) + (assoc-in [:shapes-by-id 5] {:id 5 :page 1 :group 1}))] + (with-redefs [cljs.core/random-uuid (constantly-inc 4)] + (let [result (ssh/duplicate-shapes initial [2 3])] + ;; (pprint expected) + ;; (pprint result) + (t/is (= result expected)))))) + + +;; duplicate shape: duplicate mixed bag +(t/deftest duplicate-shapes-test3 + (let [initial {:pages-by-id {1 {:id 1 :shapes [1 4]}} + :shapes-by-id {1 {:id 1 :page 1 + :type :builtin/group + :items [2 3]} + 2 {:id 2 :page 1 :group 1} + 3 {:id 3 :page 1 :group 1} + 4 {:id 4 :page 1}}} + + expected (-> initial + (assoc-in [:pages-by-id 1 :shapes] [1 4 5 6]) + (assoc-in [:shapes-by-id 5] {:id 5 :page 1}) + (assoc-in [:shapes-by-id 6] {:id 6 :page 1}))] + (with-redefs [cljs.core/random-uuid (constantly-inc 5)] + (let [result (ssh/duplicate-shapes initial [3 4])] + ;; (pprint expected) + ;; (pprint result) + (t/is (= result expected)))))) + + +;; duplicate shape: duplicate one group +(t/deftest duplicate-shapes-test4 + (let [initial {:pages-by-id {1 {:id 1 :shapes [1]}} + :shapes-by-id {1 {:id 1 :page 1 + :type :builtin/group + :items [2]} + 2 {:id 3 :page 1 :group 1}}} + + expected (-> initial + (assoc-in [:pages-by-id 1 :shapes] [1 3]) + (assoc-in [:shapes-by-id 3] {:id 3 :page 1 + :type :builtin/group + :items [4]}) + (assoc-in [:shapes-by-id 4] {:id 4 :page 1 :group 3}))] + (with-redefs [cljs.core/random-uuid (constantly-inc 3)] + (let [result (ssh/duplicate-shapes initial [1])] + ;; (pprint expected) + ;; (pprint result) + (t/is (= result expected)))))) diff --git a/test/uxbox/test_runner.cljs b/test/uxbox/test_runner.cljs index 885fb2cfd..574bd1082 100644 --- a/test/uxbox/test_runner.cljs +++ b/test/uxbox/test_runner.cljs @@ -1,5 +1,6 @@ (ns uxbox.test-runner (:require [cljs.test :as test] + [uxbox.state.shapes-tests] [uxbox.data.workspace-tests] [uxbox.util.geom-tests])) @@ -11,6 +12,7 @@ (test/empty-env) 'uxbox.data.workspace-tests 'uxbox.util.geom-tests + 'uxbox.state.shapes-tests )) (defmethod test/report [:cljs.test/default :end-run-tests]