From a3a889339bf17738fd9075f42be2898c3ab05a82 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Mon, 6 Apr 2020 11:43:37 +0200 Subject: [PATCH] :sparkles: Adds ungroup shapes --- frontend/src/uxbox/main/data/helpers.cljs | 40 +++++++++ frontend/src/uxbox/main/data/workspace.cljs | 89 ++++++++++++-------- frontend/src/uxbox/main/ui/shapes/frame.cljs | 4 +- 3 files changed, 96 insertions(+), 37 deletions(-) diff --git a/frontend/src/uxbox/main/data/helpers.cljs b/frontend/src/uxbox/main/data/helpers.cljs index e6a7c92c2a..9a3009aaac 100644 --- a/frontend/src/uxbox/main/data/helpers.cljs +++ b/frontend/src/uxbox/main/data/helpers.cljs @@ -27,3 +27,43 @@ shapes (remove #(= (:type %) :frame) (vals objects))] (some contains-shape-fn shapes))) + +(defn get-parent + "Retrieve the id of the parent for the shape-id (if exists" + [shape-id objects] + (let [check-parenthood + (fn [shape] (when (and (:shapes shape) + ((set (:shapes shape)) shape-id)) + (:id shape)))] + (some check-parenthood (vals objects)))) + +(defn replace-shapes + "Replace inside shapes the value `to-replace-id` for the value in items keeping the same order. + `to-replace-id` can be a set, a sequable or a single value. Any of these will be changed into a + set to make the replacement" + [shape to-replace-id items] + (let [should-replace + (cond + (set? to-replace-id) to-replace-id + (seqable? to-replace-id) (set to-replace-id) + :else #{to-replace-id}) + + ;; This function replaces the first ocurrence of the set `should-replace` for the + ;; value in `items`. Next elements that match are removed but not replaced again + ;; so for example: + ;; should-replace = #{2 3 5} + ;; (replace-fn [ 1 2 3 4 5] ["a" "b"] []) + ;; => [ 1 "a" "b" 4 ] + replace-fn + (fn [to-replace acc shapes] + (if (empty? shapes) + acc + (let [cur (first shapes) + rest (subvec shapes 1)] + (if (should-replace cur) + (recur [] (into acc to-replace) rest) + (recur to-replace (conj acc cur) rest))))) + + replace-shapes (partial replace-fn (if (seqable? items) items [items]) [])] + + (update shape :shapes replace-shapes))) diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 23096bcb31..7a6b28137d 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -2192,52 +2192,71 @@ (defn create-group [] (let [id (uuid/next)] (ptk/reify ::create-group - ptk/UpdateEvent - (update [_ state] + ptk/WatchEvent + (watch [_ state stream] (let [selected (get-in state [:workspace-local :selected])] - (if (and selected (-> selected count (> 1))) + (if (not-empty selected) (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) parent (get-parent (first selected) (vals objects)) + parent-id (:id parent) selected-objects (map (partial get objects) selected) selection-rect (geom/selection-rect selected-objects) frame-id (-> selected-objects first :frame-id) - new-shape (group-shape id frame-id selected selection-rect) - objects-removed (-> objects - (assoc (:id new-shape) new-shape) - (update-in [(:id parent) :shapes] - (fn [shapes] (filter #(not (selected %)) shapes))) - (update-in [(:id parent) :shapes] conj (:id new-shape)))] - (-> state - (assoc-in [:workspace-data page-id :objects] objects-removed ) - (assoc-in [:workspace-local :selected] #{(:id new-shape)}))) - state))) + group-shape (group-shape id frame-id selected selection-rect)] - ptk/WatchEvent - (watch [_ state stream] - (let [obj (get-in state [:workspace-data (::page-id state) :objects id]) - frame-id (:frame-id obj) - frame (get-in state [:workspace-data (::page-id state) :objects frame-id])] - (rx/of (commit-changes [{:type :add-obj - :id id - :frame-id (:frame-id obj) - :obj obj} - {:type :mod-obj - :id frame-id - :operations [{:type :set - :attr :shapes - :val (:shapes frame)}]}] - [{:type :del-obj :id id} - {:type :mod-obj - :id frame-id - :operations [{:type :set - :attr :shapes - :val (into (:shapes frame) (:shapes obj))}]}]))))))) + (let [updated-parent (helpers/replace-shapes parent selected id) + rchanges [{:type :add-obj + :id id + :frame-id frame-id + :obj group-shape} + {:type :mod-obj + :id parent-id + :operations [{:type :set + :attr :shapes + :val (:shapes updated-parent)}]}] + uchanges [{:type :del-obj + :id id} + {:type :mod-obj + :id parent-id + :operations [{:type :set + :attr :shapes + :val (:shapes parent)}]}]] + (rx/of (commit-changes rchanges uchanges {:commit-local? true}) + (fn [state] (assoc-in state [:workspace-local :selected] #{id}))))) + rx/empty)))))) (defn remove-group [] (ptk/reify ::remove-group - ptk/UpdateEvent - (update [_ state] state))) + ptk/WatchEvent + (watch [_ state stream] + (let [selected (get-in state [:workspace-local :selected]) + group-id (first selected) + group (get-in state [:workspace-data (::page-id state) :objects group-id])] + (if (and (= (count selected) 1) (= (:type group) :group)) + (let [objects (get-in state [:workspace-data (::page-id state) :objects]) + parent-id (helpers/get-parent group-id objects) + parent (get objects parent-id)] + (let [changed-parent (helpers/replace-shapes parent group-id (:shapes group)) + rchanges [{:type :mod-obj + :id parent-id + :operations [{:type :set :attr :shapes :val (:shapes changed-parent)}]} + + ;; Need to modify the object otherwise the children will be deleted + {:type :mod-obj + :id group-id + :operations [{:type :set :attr :shapes :val []}]} + {:type :del-obj + :id group-id}] + uchanges [{:type :add-obj + :id group-id + :frame-id (:frame-id group) + :obj group} + {:type :mod-obj + :id parent-id + :operations [{:type :set :attr :shapes :val (:shapes parent)}]}]] + (rx/of (commit-changes rchanges uchanges {:commit-local? true})))) + rx/empty))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Shortcuts diff --git a/frontend/src/uxbox/main/ui/shapes/frame.cljs b/frontend/src/uxbox/main/ui/shapes/frame.cljs index e53bd9140d..f0b4b135ed 100644 --- a/frontend/src/uxbox/main/ui/shapes/frame.cljs +++ b/frontend/src/uxbox/main/ui/shapes/frame.cljs @@ -91,11 +91,11 @@ :transform (str "scale(" inv-zoom ", " inv-zoom ") " "translate(" (* zoom (:x label-pos)) ", " (* zoom (:y label-pos)) ")") - ; User may also select the frame with single click in the label + ;; User may also select the frame with single click in the label :on-click on-double-click} (:name shape)] [:& (frame-shape shape-wrapper) {:shape shape - :childs childs}]])))) + :childs childs}]]))))) (defn frame-shape [shape-wrapper] (mf/fnc frame-shape