diff --git a/frontend/src/uxbox/main/data/shapes.cljs b/frontend/src/uxbox/main/data/shapes.cljs index 65b1a806c..43db4c574 100644 --- a/frontend/src/uxbox/main/data/shapes.cljs +++ b/frontend/src/uxbox/main/data/shapes.cljs @@ -119,69 +119,22 @@ (update-in $ [:pages page :shapes] conj shape-id)) (assoc-in $ [:shapes shape-id] 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) :group) - (let [id (uuid/random) - items (:items shape) - name (generate-unique-name state (str (:name shape) "-copy")) - shape (assoc shape - :id id - :page page - :items [] - :name name) - state (if (nil? group) - (-> state - (update-in [:pages page :shapes] - #(into [] (cons id %))) - (assoc-in [:shapes id] shape)) - (-> state - (update-in [:shapes group :items] - #(into [] (cons id %))) - (assoc-in [:shapes id] shape)))] - (->> (map #(get-in state [:shapes %]) items) - (reverse) - (reduce #(duplicate-shape %1 %2 page id) state))) - (let [id (uuid/random) - name (generate-unique-name state (str (:name shape) "-copy")) - shape (-> (dissoc shape :group) - (assoc :id id :page page :name name) - (merge (when group {:group group})))] - (if (nil? group) - (-> state - (update-in [:pages page :shapes] #(into [] (cons id %))) - (assoc-in [:shapes id] shape)) - (-> state - (update-in [:shapes group :items] #(into [] (cons id %))) - (assoc-in [:shapes id] shape))))))] - (reduce #(duplicate-shape %1 %2 page group) state shapes)))) +(defn- duplicate-shape + [state shape page] + (let [id (uuid/random) + name (generate-unique-name state (str (:name shape) "-copy")) + shape (assoc shape :id id :page page :name name)] + (-> state + (update-in [:pages page :shapes] #(into [] (cons id %))) + (assoc-in [:shapes id] shape)))) (defn duplicate-shapes ([state shapes] (duplicate-shapes state shapes nil)) ([state shapes page] - (letfn [(all-toplevel? [coll] - (every? #(nil? (:group %)) coll)) - (all-same-group? [coll] - (let [group (:group (first coll))] - (every? #(= group (:group %)) coll)))] - (let [shapes (reverse (mapv #(get-in state [:shapes %]) shapes))] - (cond - (all-toplevel? shapes) - (let [page (or page (:page (first shapes)))] - (duplicate-shapes' state shapes page)) - - (all-same-group? shapes) - (let [page (or page (:page (first shapes))) - group (:group (first shapes))] - (duplicate-shapes' state shapes page group)) - - :else - (let [page (or page (:page (first shapes)))] - (duplicate-shapes' state shapes page))))))) + (let [shapes (reverse (map #(get-in state [:shapes %]) shapes)) + page (or page (:page (first shapes)))] + (reduce #(duplicate-shape %1 %2 page) state shapes)))) ;; --- Delete Shapes @@ -194,12 +147,12 @@ (defn dissoc-from-page "Given a shape, try to remove its reference from the corresponding page." - [state {:keys [id page] :as shape}] - ;; TODO: handle canvas special case - (update-in state [:pages page :shapes] - (fn [items] (vec (remove #(= % id) items))))) - -(declare dissoc-shape) + [state {:keys [id page type] :as shape}] + (if (= :canvas type) + (update-in state [:pages page :canvas] + (fn [items] (vec (remove #(= % id) items)))) + (update-in state [:pages page :shapes] + (fn [items] (vec (remove #(= % id) items)))))) (defn dissoc-shape "Given a shape, removes it from the state." @@ -208,109 +161,22 @@ (dissoc-from-page shape) (dissoc-from-index shape))) -;; --- Shape Movements +;; --- Shape Vertical Ordering -(defn- drop-at-index - [index coll v] - (let [[fst snd] (split-at index coll)] - (into [] (concat fst [v] snd)))) - -(defn drop-relative - [state loc sid] - {:pre [(not (nil? sid))]} - (let [shape (get-in state [:shapes (first sid)]) - {:keys [page group]} shape - sid (:id shape) - - shapes (if group - (get-in state [:shapes group :items]) - (get-in state [:pages page :shapes])) - - index (case loc - :first 0 - :after (min (- (count shapes) 1) (inc (index-of shapes sid))) - :before (max 0 (- (index-of shapes sid) 1)) - :last (- (count shapes) 1)) - - state (-> state - (dissoc-from-page shape)) - - shapes (if group - (get-in state [:shapes group :items]) - (get-in state [:pages page :shapes])) - - shapes (drop-at-index index shapes sid)] - - (if group - (as-> state $ - (assoc-in $ [:shapes group :items] shapes) - (update-in $ [:shapes sid] assoc :group group)) - (as-> state $ - (assoc-in $ [:pages page :shapes] shapes) - (update-in $ [:shapes sid] dissoc :group))))) - -(defn drop-aside - [state loc tid sid] - {:pre [(not= tid sid) - (not (nil? tid)) - (not (nil? sid))]} - (let [{:keys [page group]} (get-in state [:shapes tid]) - source (get-in state [:shapes sid]) - - state (-> state - (dissoc-from-page source)) - - shapes (if group - (get-in state [:shapes group :items]) - (get-in state [:pages page :shapes])) - - index (case loc - :after (inc (index-of shapes tid)) - :before (index-of shapes tid)) - - shapes (drop-at-index index shapes sid)] - (if group - (as-> state $ - (assoc-in $ [:shapes group :items] shapes) - (update-in $ [:shapes sid] assoc :group group)) - (as-> state $ - (assoc-in $ [:pages page :shapes] shapes) - (update-in $ [:shapes sid] dissoc :group))))) - -(def drop-after #(drop-aside %1 :after %2 %3)) -(def drop-before #(drop-aside %1 :before %2 %3)) - -(defn drop-inside - [state tid sid] - {:pre [(not= tid sid)]} - (let [source (get-in state [:shapes sid]) - state (-> state - (dissoc-from-page source)) - shapes (get-in state [:shapes tid :items])] - (if (seq shapes) - (as-> state $ - (assoc-in $ [:shapes tid :items] (conj shapes sid)) - (update-in $ [:shapes sid] assoc :group tid)) - state))) - -(defn drop-shape - [state sid tid loc] - (if (= tid sid) - state - (case loc - :inside (drop-inside state tid sid) - :before (drop-before state tid sid) - :after (drop-after state tid sid) - (throw (ex-info "Invalid data" {}))))) - -(defn move-layer - [state shape loc] - (case loc - :up (drop-relative state :before shape) - :down (drop-relative state :after shape) - :top (drop-relative state :first shape) - :bottom (drop-relative state :last shape) - (throw (ex-info "Invalid data" {})))) +(defn order-shape + [state sid opt] + (let [{:keys [page] :as shape} (get-in state [:shapes sid]) + shapes (get-in state [:pages page :shapes]) + index (case opt + :top 0 + :down (min (- (count shapes) 1) (inc (index-of shapes sid))) + :up (max 0 (- (index-of shapes sid) 1)) + :bottom (- (count shapes) 1))] + (update-in state [:pages page :shapes] + (fn [items] + (let [[fst snd] (->> (remove #(= % sid) items) + (split-at index))] + (into [] (concat fst [sid] snd))))))) ;; --- Shape Selection @@ -323,13 +189,6 @@ (geom/overlaps? shape selrect) (conj acc id) - (:locked shape) - acc - - (= type :group) - (reduce (partial try-match-shape xf selrect) - acc (sequence xf items)) - :else acc)) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index a6515802d..d1e69888b 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -331,11 +331,12 @@ ;; --- Duplicate Selected (def duplicate-selected - (reify + (ptk/reify ::duplicate-selected udp/IPageUpdate ptk/UpdateEvent (update [_ state] - (let [selected (get-in state [:workspace :selected])] + (let [pid (get-in state [:workspace :current]) + selected (get-in state [:workspace pid :selected])] (ds/duplicate-shapes state selected))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -510,16 +511,20 @@ ;; --- Move Selected Layer -(defn move-selected-layer +(defn order-selected-shapes [loc] - (assert (s/valid? ::direction loc)) - (reify + (s/assert ::direction loc) + (ptk/reify ::move-selected-layer udp/IPageUpdate ptk/UpdateEvent (update [_ state] (let [id (get-in state [:workspace :current]) selected (get-in state [:workspace id :selected])] - (ds/move-layer state selected loc))))) + (prn "order-selected-shapes" selected) + ;; NOTE: multiple selection ordering not supported + (if (pos? (count selected)) + (ds/order-shape state (first selected) loc) + state))))) ;; --- Update Shape Position diff --git a/frontend/src/uxbox/main/ui/workspace/shortcuts.cljs b/frontend/src/uxbox/main/ui/workspace/shortcuts.cljs index b8712e6df..76c5851d2 100644 --- a/frontend/src/uxbox/main/ui/workspace/shortcuts.cljs +++ b/frontend/src/uxbox/main/ui/workspace/shortcuts.cljs @@ -42,10 +42,10 @@ :ctrl+t #(st/emit! (dw/select-for-drawing :text)) :esc #(st/emit! (dw/deselect-all)) :delete #(st/emit! dw/delete-selected) - :ctrl+up #(st/emit! (dw/move-selected-layer :up)) - :ctrl+down #(st/emit! (dw/move-selected-layer :down)) - :ctrl+shift+up #(st/emit! (dw/move-selected-layer :top)) - :ctrl+shift+down #(st/emit! (dw/move-selected-layer :bottom)) + :ctrl+up #(st/emit! (dw/order-selected-shapes :up)) + :ctrl+down #(st/emit! (dw/order-selected-shapes :down)) + :ctrl+shift+up #(st/emit! (dw/order-selected-shapes :top)) + :ctrl+shift+down #(st/emit! (dw/order-selected-shapes :bottom)) :shift+up #(st/emit! (dw/move-selected :up :fast)) :shift+down #(st/emit! (dw/move-selected :down :fast)) :shift+right #(st/emit! (dw/move-selected :right :fast)) diff --git a/frontend/test/uxbox/tests/test_main_data_shapes_impl.cljs b/frontend/test/uxbox/tests/test_main_data_shapes_impl.cljs index a9385679a..57042943a 100644 --- a/frontend/test/uxbox/tests/test_main_data_shapes_impl.cljs +++ b/frontend/test/uxbox/tests/test_main_data_shapes_impl.cljs @@ -100,85 +100,6 @@ ;; (pprint result) (t/is (= result expected)))))) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Drop Shape (drag and drop and sorted) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; drop shape: move shape before other shape - -(t/deftest drop-shape-test1 - (let [initial {:pages {1 {:id 1 :shapes [1 2 3]}} - :shapes {1 {:id 1 :page 1} - 2 {:id 2 :page 1} - 3 {:id 3 :page 1}}} - expected {:pages {1 {:id 1, :shapes [3 1 2]}}, - :shapes {1 {:id 1, :page 1}, - 2 {:id 2, :page 1}, - 3 {:id 3, :page 1}}} - result (impl/drop-shape initial 3 1 :before)] - ;; (pprint expected) - ;; (pprint result) - (t/is (= result expected)) - (t/is (vector? (get-in result [:pages 1 :shapes]))))) - -;; drop shape: move shape after other shape - -(t/deftest drop-shape-test2 - (let [initial {:pages {1 {:id 1 :shapes [1 2 3]}} - :shapes {1 {:id 1 :page 1} - 2 {:id 2 :page 1} - 3 {:id 3 :page 1}}} - - expected {:pages {1 {:id 1, :shapes [1 3 2]}}, - :shapes {1 {:id 1, :page 1}, - 2 {:id 2, :page 1}, - 3 {:id 3, :page 1}}} - result (impl/drop-shape initial 3 1 :after)] - ;; (pprint expected) - ;; (pprint result) - (t/is (= result expected)) - (t/is (vector? (get-in result [:pages 1 :shapes]))))) - -;; drop shape: move shape before other shape that is part of group. - -(t/deftest drop-shape-test3 - (let [initial {:pages {1 {:id 1 :shapes [1 3 4]}} - :shapes {1 {:id 1 :page 1 :type :group :items [2]} - 2 {:id 2 :page 1 :group 1} - 3 {:id 3 :page 1} - 4 {:id 4 :page 1}}} - - expected {:pages {1 {:id 1, :shapes [1 4]}}, - :shapes {1 {:id 1, :page 1, :type :group, :items [3 2]}, - 2 {:id 2, :page 1, :group 1}, - 3 {:id 3, :page 1, :group 1}, - 4 {:id 4, :page 1}}} - - result (impl/drop-shape initial 3 2 :before)] - ;; (pprint expected) - ;; (pprint result) - (t/is (= result expected)) - (t/is (vector? (get-in result [:pages 1 :shapes]))))) - -;; drop shape: move shape inside group - -(t/deftest drop-shape-test4 - (let [initial {:pages {1 {:id 1 :shapes [1 3 4]}} - :shapes {1 {:id 1 :page 1 :type :group :items [2]} - 2 {:id 2 :page 1 :group 1} - 3 {:id 3 :page 1} - 4 {:id 4 :page 1}}} - expected {:pages {1 {:id 1, :shapes [1 4]}}, - :shapes {1 {:id 1, :page 1, :type :group, :items [2 3]}, - 2 {:id 2, :page 1, :group 1}, - 3 {:id 3, :page 1, :group 1}, - 4 {:id 4, :page 1}}} - result (impl/drop-shape initial 3 1 :inside)] - ;; (pprint expected) - ;; (pprint result) - (t/is (= result expected)) - (t/is (vector? (get-in result [:pages 1 :shapes]))))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Delete Shape ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;