diff --git a/backend/tests/uxbox/tests/test_common_pages.clj b/backend/tests/uxbox/tests/test_common_pages.clj index 9eac86a80..5f48b90d9 100644 --- a/backend/tests/uxbox/tests/test_common_pages.clj +++ b/backend/tests/uxbox/tests/test_common_pages.clj @@ -77,16 +77,23 @@ (t/testing "Adds single object" (let [chg {:type :add-obj :id id-a + :parent-id uuid/zero :frame-id uuid/zero :obj {:id id-a :frame-id uuid/zero + :parent-id uuid/zero :type :rect :name "rect"}} res (cp/process-changes data [chg])] + ;; (clojure.pprint/pprint data) + ;; (clojure.pprint/pprint res) + (t/is (= 2 (count (:objects res)))) (t/is (= (:obj chg) (get-in res [:objects id-a]))) (t/is (= [id-a] (get-in res [:objects uuid/zero :shapes]))))) + + (t/testing "Adds several objects with different indexes" (let [data cp/default-page-data @@ -277,36 +284,77 @@ rect-c-id (uuid/custom 7) rect-d-id (uuid/custom 8) rect-e-id (uuid/custom 9) - data (-> cp/default-page-data - (assoc-in [cp/root :shapes] [frame-a-id]) - (assoc-in [:objects frame-a-id] - {:id frame-a-id :name "Frame a" :type :frame}) - (assoc-in [:objects frame-b-id] - {:id frame-b-id :name "Frame b" :type :frame}) - ;; Groups - (assoc-in [:objects group-a-id] - {:id group-a-id :name "Group A" :type :group :frame-id frame-a-id}) - (assoc-in [:objects group-b-id] - {:id group-b-id :name "Group B" :type :group :frame-id frame-a-id}) + data + (-> cp/default-page-data + (assoc-in [:objects uuid/zero :shapes] [frame-a-id frame-b-id]) + (assoc-in [:objects frame-a-id] + {:id frame-a-id + :parent-id uuid/zero + :frame-id uuid/zero + :name "Frame a" + :shapes [group-a-id group-b-id rect-e-id] + :type :frame}) + + (assoc-in [:objects frame-b-id] + {:id frame-b-id + :parent-id uuid/zero + :frame-id uuid/zero + :name "Frame b" + :shapes [] + :type :frame}) + + ;; Groups + (assoc-in [:objects group-a-id] + {:id group-a-id + :name "Group A" + :type :group + :parent-id frame-a-id + :frame-id frame-a-id + :shapes [rect-a-id rect-b-id rect-c-id]}) + (assoc-in [:objects group-b-id] + {:id group-b-id + :name "Group B" + :type :group + :parent-id frame-a-id + :frame-id frame-a-id + :shapes [rect-d-id]}) ;; Shapes - (assoc-in [:objects rect-a-id] - {:id rect-a-id :name "Rect A" :type :rect :frame-id frame-a-id}) - (assoc-in [:objects rect-b-id] - {:id rect-b-id :name "Rect B" :type :rect :frame-id frame-a-id}) - (assoc-in [:objects rect-c-id] - {:id rect-c-id :name "Rect C" :type :rect :frame-id frame-a-id}) - (assoc-in [:objects rect-d-id] - {:id rect-d-id :name "Rect D" :type :rect :frame-id frame-a-id}) - (assoc-in [:objects rect-e-id] - {:id rect-e-id :name "Rect E" :type :rect :frame-id frame-a-id}) + (assoc-in [:objects rect-a-id] + {:id rect-a-id + :name "Rect A" + :type :rect + :parent-id group-a-id + :frame-id frame-a-id}) - ;; Relationships - (assoc-in [:objects cp/root :shapes] [frame-a-id frame-b-id]) - (assoc-in [:objects frame-a-id :shapes] [group-a-id group-b-id rect-e-id]) - (assoc-in [:objects group-a-id :shapes] [rect-a-id rect-b-id rect-c-id]) - (assoc-in [:objects group-b-id :shapes] [rect-d-id]))] + (assoc-in [:objects rect-b-id] + {:id rect-b-id + :name "Rect B" + :type :rect + :parent-id group-a-id + :frame-id frame-a-id}) + + (assoc-in [:objects rect-c-id] + {:id rect-c-id + :name "Rect C" + :type :rect + :parent-id group-a-id + :frame-id frame-a-id}) + + (assoc-in [:objects rect-d-id] + {:id rect-d-id + :name "Rect D" + :parent-id group-b-id + :type :rect + :frame-id frame-a-id}) + + (assoc-in [:objects rect-e-id] + {:id rect-e-id + :name "Rect E" + :type :rect + :parent-id frame-a-id + :frame-id frame-a-id}))] (t/testing "Create new group an add objects from the same group" (let [new-group-id (uuid/next) @@ -322,6 +370,10 @@ :shapes [rect-b-id rect-c-id]}] res (cp/process-changes data changes)] + ;; (clojure.pprint/pprint data) + ;; (println "===============") + ;; (clojure.pprint/pprint res) + (t/is (= [group-a-id group-b-id rect-e-id new-group-id] (get-in res [:objects frame-a-id :shapes]))) (t/is (= [rect-b-id rect-c-id] @@ -396,10 +448,15 @@ (t/testing "Move elements to frame zero" (let [changes [{:type :mov-objects - :parent-id cp/root + :parent-id uuid/zero :shapes [group-a-id] :index 0}] res (cp/process-changes data changes)] + + ;; (pprint (get-in data [:objects uuid/zero])) + ;; (println "==========") + ;; (pprint (get-in res [:objects uuid/zero])) + (t/is (= [group-a-id frame-a-id frame-b-id] (get-in res [:objects cp/root :shapes]))))) @@ -408,7 +465,8 @@ :parent-id group-a-id :shapes [group-a-id]}] res (cp/process-changes data changes)] - (t/is (= data res)))))) + (t/is (= data res)))) + )) (t/deftest process-change-move-objects-regression @@ -653,4 +711,57 @@ )) +(t/deftest idenpotency-regression-1 + (let [data {:version 5 + :objects + {#uuid "00000000-0000-0000-0000-000000000000" + {:id #uuid "00000000-0000-0000-0000-000000000000", + :type :frame, + :name "root", + :shapes + [#uuid "f5d51910-ab23-11ea-ac38-e1abed64181a" + #uuid "f6a36590-ab23-11ea-ac38-e1abed64181a"]}, + #uuid "f5d51910-ab23-11ea-ac38-e1abed64181a" + {:name "Rect-1", + :type :rect, + :id #uuid "f5d51910-ab23-11ea-ac38-e1abed64181a", + :parent-id #uuid "00000000-0000-0000-0000-000000000000", + :frame-id #uuid "00000000-0000-0000-0000-000000000000"} + #uuid "f6a36590-ab23-11ea-ac38-e1abed64181a" + {:name "Rect-2", + :type :rect, + :id #uuid "f6a36590-ab23-11ea-ac38-e1abed64181a", + :parent-id #uuid "00000000-0000-0000-0000-000000000000", + :frame-id #uuid "00000000-0000-0000-0000-000000000000"}}} + chgs [{:type :add-obj, + :id #uuid "3375ec40-ab24-11ea-b512-b945e8edccf5", + :frame-id #uuid "00000000-0000-0000-0000-000000000000", + :index 0 + :obj {:name "Group-1", + :type :group, + :id #uuid "3375ec40-ab24-11ea-b512-b945e8edccf5", + :frame-id #uuid "00000000-0000-0000-0000-000000000000"}} + {:type :mov-objects, + :parent-id #uuid "3375ec40-ab24-11ea-b512-b945e8edccf5", + :shapes + [#uuid "f5d51910-ab23-11ea-ac38-e1abed64181a" + #uuid "f6a36590-ab23-11ea-ac38-e1abed64181a"]}] + + res1 (cp/process-changes data chgs) + res2 (cp/process-changes res1 chgs)] + + ;; (clojure.pprint/pprint data) + ;; (println "==============") + ;; (clojure.pprint/pprint res2) + + (t/is (= [#uuid "f5d51910-ab23-11ea-ac38-e1abed64181a" + #uuid "f6a36590-ab23-11ea-ac38-e1abed64181a"] + (get-in data [:objects uuid/zero :shapes]))) + (t/is (= [#uuid "3375ec40-ab24-11ea-b512-b945e8edccf5"] + (get-in res2 [:objects uuid/zero :shapes]))) + (t/is (= [#uuid "3375ec40-ab24-11ea-b512-b945e8edccf5"] + (get-in res1 [:objects uuid/zero :shapes]))) + )) + + diff --git a/common/uxbox/common/geom/shapes.cljc b/common/uxbox/common/geom/shapes.cljc index fb783daca..f67b65ce3 100644 --- a/common/uxbox/common/geom/shapes.cljc +++ b/common/uxbox/common/geom/shapes.cljc @@ -10,8 +10,8 @@ (ns uxbox.common.geom.shapes (:require [clojure.spec.alpha :as s] - [uxbox.common.pages :as cp] [uxbox.common.spec :as us] + [uxbox.common.pages-helpers :as cph] [uxbox.common.geom.matrix :as gmt] [uxbox.common.geom.point :as gpt] [uxbox.common.math :as mth] @@ -61,7 +61,7 @@ (defn recursive-move "Move the shape and all its recursive children." [shape dpoint objects] - (let [children-ids (cp/get-children (:id shape) objects) + (let [children-ids (cph/get-children (:id shape) objects) children (map #(get objects %) children-ids)] (map #(move % dpoint) (cons shape children)))) @@ -253,6 +253,7 @@ ;; -- Points (declare transform-shape-point) + (defn shape->points [shape] (let [points (case (:type shape) diff --git a/common/uxbox/common/pages.cljc b/common/uxbox/common/pages.cljc index 6054169da..5ed758798 100644 --- a/common/uxbox/common/pages.cljc +++ b/common/uxbox/common/pages.cljc @@ -11,119 +11,15 @@ "A common (clj/cljs) functions and specs for pages." (:require [clojure.spec.alpha :as s] - [uxbox.common.uuid :as uuid] [uxbox.common.data :as d] + [uxbox.common.pages-helpers :as cph] [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us])) + [uxbox.common.geom.shapes :as geom] + [uxbox.common.spec :as us] + [uxbox.common.uuid :as uuid])) (def page-version 5) -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Page Data Structure Helpers -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; - -(defn get-children - "Retrieve all children ids recursively for a given object" - [id objects] - (let [shapes (get-in objects [id :shapes])] - (if shapes - (d/concat shapes (mapcat #(get-children % objects) shapes)) - []))) - -(defn is-shape-grouped - "Checks if a shape is inside a group" - [shape-id objects] - (let [contains-shape-fn (fn [{:keys [shapes]}] ((set shapes) shape-id)) - 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 calculate-child-parent-map - [objects] - (let [red-fn - (fn [acc {:keys [id shapes]}] - ;; Insert every pair shape -> parent into accumulated value - (into acc (map #(vector % id) (or shapes []))))] - (reduce red-fn {} (vals objects)))) - -(defn get-all-parents - [shape-id objects] - (let [child->parent (calculate-child-parent-map objects) - rec-fn (fn [cur result] - (if-let [parent (child->parent cur)] - (recur parent (conj result parent)) - (vec (reverse result))))] - (rec-fn shape-id []))) - -(defn- calculate-invalid-targets - [shape-id objects] - (let [result #{shape-id} - children (get-in objects [shape-id :shape]) - reduce-fn (fn [result child-id] - (into result (calculate-invalid-targets child-id objects)))] - (reduce reduce-fn result children))) - -(defn- valid-frame-target - [shape-id parent-id objects] - (let [shape (get objects shape-id)] - (or (not= (:type shape) :frame) - (= parent-id uuid/zero)))) - -(defn- insert-at-index - [shapes index ids] - (let [[before after] (split-at index shapes) - p? (set ids)] - (d/concat [] - (remove p? before) - ids - (remove p? after)))) - -(defn select-toplevel-shapes - ([objects] (select-toplevel-shapes objects nil)) - ([objects {:keys [include-frames?] :or {include-frames? false}}] - (let [lookup #(get objects %) - root (lookup uuid/zero) - childs (:shapes root)] - (loop [id (first childs) - ids (rest childs) - res []] - (if (nil? id) - res - (let [obj (lookup id) - typ (:type obj)] - (recur (first ids) - (rest ids) - (if (= :frame typ) - (if include-frames? - (d/concat res [obj] (map lookup (:shapes obj))) - (d/concat res (map lookup (:shapes obj)))) - (conj res obj))))))))) - -(defn select-frames - [objects] - (let [root (get objects uuid/zero) - loopfn (fn loopfn [ids] - (let [obj (get objects (first ids))] - (cond - (nil? obj) - nil - - (= :frame (:type obj)) - (lazy-seq (cons obj (loopfn (rest ids)))) - - :else - (lazy-seq (loopfn (rest ids))))))] - (loopfn (:shapes root)))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Page Transformation Changes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -258,6 +154,7 @@ ::options ::objects])) +(s/def ::ids (s/coll-of ::us/uuid)) (s/def ::attr keyword?) (s/def ::val any?) (s/def ::frame-id uuid?) @@ -297,6 +194,10 @@ (s/keys :req-un [::id] :opt-un [::session-id])) +(defmethod change-spec-impl :reg-obj [_] + (s/keys :req-un [::ids] + :opt-un [::session-id])) + (defmethod change-spec-impl :mov-objects [_] (s/keys :req-un [::parent-id ::shapes] :opt-un [::index])) @@ -402,8 +303,6 @@ (or (process-change %1 %2) %1)) data))) -(declare insert-at-index) - (defmethod process-change :set-option [data {:keys [option value]}] (let [path (if (seqable? option) option [option])] @@ -418,8 +317,9 @@ (when (and (contains? objects parent-id) (contains? objects frame-id)) (let [obj (assoc obj - :frame-id frame-id - :id id)] + :frame-id frame-id + :parent-id parent-id + :id id)] (-> data (update :objects assoc id obj) (update-in [:objects parent-id :shapes] @@ -428,7 +328,7 @@ (cond (some #{id} shapes) shapes (nil? index) (conj shapes id) - :else (insert-at-index shapes index [id])))))))))) + :else (cph/insert-at-index shapes index [id])))))))))) (defmethod process-change :mod-obj [data {:keys [id operations] :as change}] @@ -442,7 +342,7 @@ [data {:keys [id] :as change}] (when-let [{:keys [frame-id shapes] :as obj} (get-in data [:objects id])] (let [objects (:objects data) - parent-id (get-parent id objects) + parent-id (cph/get-parent id objects) parent (get objects parent-id) data (update data :objects dissoc id)] (cond-> data @@ -456,15 +356,44 @@ (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]}] + (let [objects (:objects data)] + (loop [ids ids data data] + (if (seq ids) + (let [item (get objects (first ids))] + (if (= :group (:type item)) + (recur + (rest ids) + (update-in data [:objects (:id item)] + (fn [{:keys [shapes] :as obj}] + (let [shapes (->> shapes + (map (partial get objects)) + (filter identity))] + (if (seq shapes) + (let [selrect (geom/selection-rect shapes)] + (as-> obj $ + (assoc $ + :x (:x selrect) + :y (:y selrect) + :width (:width selrect) + :height (:height selrect)) + (assoc $ :points (geom/shape->points $)) + (assoc $ :selrect (geom/points->selrect (:points $))))) + obj))))) + (recur (rest ids) data))) + data)))) + (defmethod process-change :mov-objects [data {:keys [parent-id shapes index] :as change}] - (let [child->parent (calculate-child-parent-map (:objects data)) + (let [ ;; Check if the move from shape-id -> parent-id is valid + is-valid-move (fn [shape-id] - (let [invalid-targets (calculate-invalid-targets shape-id (:objects data))] + (let [invalid-targets (cph/calculate-invalid-targets shape-id (:objects data))] (and (not (invalid-targets parent-id)) - (valid-frame-target shape-id parent-id (:objects data))))) + (cph/valid-frame-target shape-id parent-id (:objects data))))) valid? (every? is-valid-move shapes) @@ -473,7 +402,7 @@ (fn [prev-shapes] (let [prev-shapes (or prev-shapes [])] (if index - (insert-at-index prev-shapes index shapes) + (cph/insert-at-index prev-shapes index shapes) (reduce (fn [acc id] (if (some #{id} acc) acc @@ -485,23 +414,31 @@ (fn [id] (fn [coll] (filterv #(not= % id) coll))) - ;; Remove from the old :shapes the references that have been moved - remove-in-parent - (fn [data shape-id] - (let [parent-id' (get child->parent shape-id)] + cpindex + (reduce + (fn [index id] + (let [obj (get-in data [:objects id])] + (assoc index id (:parent-id obj)))) + {} (keys (:objects data))) + + remove-from-old-parent + (fn remove-from-old-parent [data shape-id] + (let [prev-parent-id (get cpindex shape-id)] ;; Do nothing if the parent id of the shape is the same as ;; the new destination target parent id. - (if (= parent-id' parent-id) + (if (= prev-parent-id parent-id) data - (let [parent (-> (get-in data [:objects parent-id']) - (update :shapes (strip-id shape-id)))] - ;; When the group is empty we should remove it - (if (and (= :group (:type parent)) - (empty? (:shapes parent))) - (-> data - (update :objects dissoc (:id parent)) - (update-in [:objects (:frame-id parent) :shapes] (strip-id (:id parent)))) - (update data :objects assoc parent-id' parent)))))) + (loop [sid shape-id + pid prev-parent-id + data data] + (let [obj (get-in data [:objects pid])] + (if (and (= 1 (count (:shapes obj))) + (= sid (first (:shapes obj))) + (= :group (:type obj))) + (recur pid + (:parent-id obj) + (update data :objects dissoc pid)) + (update-in data [:objects pid :shapes] (strip-id sid)))))))) parent (get-in data [:objects parent-id]) frame (if (= :frame (:type parent)) @@ -510,11 +447,16 @@ frame-id (:id frame) + ;; Update parent-id references. + update-parent-id + (fn [data id] + (update-in data [:objects id] assoc :parent-id parent-id)) + ;; Updates the frame-id references that might be outdated update-frame-ids (fn update-frame-ids [data id] (let [data (assoc-in data [:objects id :frame-id] frame-id) - obj (get-in data [:objects id])] + obj (get-in data [:objects id])] (cond-> data (not= :frame (:type obj)) (as-> $$ (reduce update-frame-ids $$ (:shapes obj))))))] @@ -522,9 +464,11 @@ (when valid? (as-> data $ (update-in $ [:objects parent-id :shapes] insert-items) - (reduce remove-in-parent $ shapes) + (reduce update-parent-id $ shapes) + (reduce remove-from-old-parent $ shapes) (reduce update-frame-ids $ (get-in $ [:objects parent-id :shapes])))))) + (defmethod process-operation :set [shape op] (let [attr (:attr op) diff --git a/common/uxbox/common/pages_migrations.cljc b/common/uxbox/common/pages_migrations.cljc index 4fc0a6ca9..02b8ac133 100644 --- a/common/uxbox/common/pages_migrations.cljc +++ b/common/uxbox/common/pages_migrations.cljc @@ -1,6 +1,6 @@ (ns uxbox.common.pages-migrations (:require - [uxbox.common.pages :as p] + [uxbox.common.pages :as cp] [uxbox.common.geom.shapes :as gsh] [uxbox.common.geom.point :as gpt] [uxbox.common.geom.matrix :as gmt] @@ -8,15 +8,17 @@ [uxbox.common.uuid :as uuid] [uxbox.common.data :as d])) +;; TODO: revisit this + (defmulti migrate :version) (defn migrate-data ([data] - (if (= (:version data) p/page-version) + (if (= (:version data) cp/page-version) data (reduce #(migrate-data %1 %2 (inc %2)) data - (range (:version data 0) p/page-version)))) + (range (:version data 0) cp/page-version)))) ([data from-version to-version] (-> data diff --git a/frontend/src/uxbox/main/data/workspace.cljs b/frontend/src/uxbox/main/data/workspace.cljs index 0e12717ce..d5b4f16ff 100644 --- a/frontend/src/uxbox/main/data/workspace.cljs +++ b/frontend/src/uxbox/main/data/workspace.cljs @@ -16,6 +16,7 @@ [uxbox.common.data :as d] [uxbox.common.exceptions :as ex] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.common.spec :as us] [uxbox.common.uuid :as uuid] [uxbox.config :as cfg] @@ -212,7 +213,7 @@ (initialize [state local] (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - shapes (cp/select-toplevel-shapes objects {:include-frames? true}) + shapes (cph/select-toplevel-shapes objects {:include-frames? true}) srect (geom/selection-rect shapes) local (assoc local :vport size)] (cond @@ -286,7 +287,7 @@ (let [page-id (:current-page-id state) objects (get-in state [:workspace-data page-id :objects]) groups-to-adjust (->> ids - (mapcat #(reverse (cp/get-all-parents % objects))) + (mapcat #(cph/get-parents % objects)) (map #(get objects %)) (filter #(= (:type %) :group)) (map #(:id %)) @@ -405,7 +406,7 @@ (update [_ state] (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - shapes (cp/select-toplevel-shapes objects {:include-frames? true}) + shapes (cph/select-toplevel-shapes objects {:include-frames? true}) srect (geom/selection-rect shapes)] (if (or (mth/nan? (:width srect)) @@ -481,7 +482,7 @@ unames (retrieve-used-names objects) name (generate-unique-name unames (:name shape)) - frames (cp/select-frames objects) + frames (cph/select-frames objects) frame-id (if (= :frame (:type shape)) uuid/zero @@ -578,7 +579,7 @@ grouped #{:frame :group}] (update-in state [:workspace-data page-id :objects] (fn [objects] - (->> (d/concat [id] (cp/get-children id objects)) + (->> (d/concat [id] (cph/get-children id objects)) (map #(get objects %)) (remove #(grouped (:type %))) (reduce #(update %1 (:id %2) update-shape) objects))))))))) @@ -647,6 +648,7 @@ :right (gpt/point 1 0))) ;; --- Delete Selected + (defn- delete-shapes [ids] (us/assert (s/coll-of ::us/uuid) ids) @@ -655,38 +657,57 @@ (watch [_ state stream] (let [page-id (:current-page-id state) objects (get-in state [:workspace-data page-id :objects]) - cpindex (cp/calculate-child-parent-map objects) del-change #(array-map :type :del-obj :id %) + reg-change #(array-map :type :reg-obj :id %) get-empty-parents - (fn get-empty-parents [id] - (let [parent (get objects (get cpindex id))] - (if (and (= :group (:type parent)) - (= 1 (count (:shapes parent)))) - (lazy-seq (cons (:id parent) - (get-empty-parents (:id parent)))) - nil))) + (fn get-empty-parents [parents] + (->> parents + (map (fn [id] + (let [obj (get objects id)] + (when (and (= :group (:type obj)) + (= 1 (count (:shapes obj)))) + obj)))) + (take-while (complement nil?)) + (map :id))) rchanges (reduce (fn [res id] - (let [chd (cp/get-children id objects)] - (into res (d/concat - (mapv del-change (reverse chd)) - [(del-change id)] - (map del-change (get-empty-parents id)))))) + (let [children (cph/get-children id objects) + parents (cph/get-parents id objects)] + (d/concat res + (map del-change (reverse children)) + [(del-change id)] + (map del-change (get-empty-parents parents)) + [{:type :reg-obj :ids parents}]))) [] ids) uchanges - (mapv (fn [id] - (let [obj (get objects id)] - {:type :add-obj - :id id - :frame-id (:frame-id obj) - :parent-id (get cpindex id) - :obj obj})) - (reverse (map :id rchanges)))] + (reduce (fn [res id] + (let [children (cph/get-children id objects) + parents (cph/get-parents id objects) + add-chg (fn [id] + (let [item (get objects id)] + {:type :add-obj + :id (:id item) + :frame-id (:frame-id item) + :parent-id (:parent-id item) + :obj item}))] + (d/concat res + (map add-chg (reverse (get-empty-parents parents))) + [(add-chg id)] + (map add-chg children) + [{:type :reg-obj :ids parents}]))) + [] + ids) + ] + + ;; (println "================ rchanges") + ;; (cljs.pprint/pprint rchanges) + ;; (println "================ uchanges") + ;; (cljs.pprint/pprint uchanges) (rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})))))) (def delete-selected @@ -764,7 +785,7 @@ (watch [_ state stream] (let [page-id (:current-page-id state) objects (get-in state [:workspace-data page-id :objects]) - parent (get objects (cp/get-parent 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 @@ -992,7 +1013,7 @@ (update [_ state] (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - childs (cp/get-children id objects)] + childs (cph/get-children id objects)] (update-in state [:workspace-data page-id :objects] (fn [objects] (reduce (fn [objects id] @@ -1215,7 +1236,6 @@ (when (not-empty selected) (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - selected-objects (map (partial get objects) selected) selrect (geom/selection-rect selected-objects) frame-id (-> selected-objects first :frame-id) @@ -1226,7 +1246,6 @@ (map-indexed vector) (filter #(selected (second %))) (ffirst)) - rchanges [{:type :add-obj :id id :frame-id frame-id @@ -1255,7 +1274,7 @@ (when (and (= 1 (count selected)) (= (:type group) :group)) (let [shapes (:shapes group) - parent-id (cp/get-parent group-id objects) + parent-id (cph/get-parent group-id objects) parent (get objects parent-id) index-in-parent (->> (:shapes parent) (map-indexed vector) diff --git a/frontend/src/uxbox/main/data/workspace/common.cljs b/frontend/src/uxbox/main/data/workspace/common.cljs index fea80b434..5f97ce2b0 100644 --- a/frontend/src/uxbox/main/data/workspace/common.cljs +++ b/frontend/src/uxbox/main/data/workspace/common.cljs @@ -15,6 +15,7 @@ [potok.core :as ptk] [uxbox.common.data :as d] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.common.spec :as us] [uxbox.common.uuid :as uuid] [uxbox.main.worker :as uw] @@ -202,8 +203,8 @@ (let [page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - shapes (cp/select-toplevel-shapes objects) - frames (cp/select-frames objects) + shapes (cph/select-toplevel-shapes objects) + frames (cph/select-frames objects) [rch uch] (calculate-shape-to-frame-relationship-changes frames shapes)] (when-not (empty? rch) @@ -212,7 +213,7 @@ (defn get-frame-at-point [objects point] - (let [frames (cp/select-frames objects)] + (let [frames (cph/select-frames objects)] (loop [frame (first frames) rest (rest frames)] (d/seek #(geom/has-point? % point) frames)))) @@ -306,7 +307,7 @@ (let [expand-fn (fn [expanded] (merge expanded (->> ids - (map #(cp/get-all-parents % objects)) + (map #(cph/get-parents % objects)) flatten (filter #(not= % uuid/zero)) (map (fn [id] {id true})) diff --git a/frontend/src/uxbox/main/data/workspace/drawing.cljs b/frontend/src/uxbox/main/data/workspace/drawing.cljs index 803d12180..c42a1fc88 100644 --- a/frontend/src/uxbox/main/data/workspace/drawing.cljs +++ b/frontend/src/uxbox/main/data/workspace/drawing.cljs @@ -16,6 +16,7 @@ [uxbox.common.geom.shapes :as geom] [uxbox.common.geom.point :as gpt] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.util.geom.path :as path] [uxbox.main.snap :as snap] [uxbox.main.streams :as ms] @@ -95,7 +96,7 @@ objects (get-in state [:workspace-data page-id :objects]) layout (get state :workspace-layout) - frames (cp/select-frames objects) + frames (cph/select-frames objects) fid (or (->> frames (filter #(geom/has-point? % initial)) first diff --git a/frontend/src/uxbox/main/data/workspace/selection.cljs b/frontend/src/uxbox/main/data/workspace/selection.cljs index 63642a807..10d634899 100644 --- a/frontend/src/uxbox/main/data/workspace/selection.cljs +++ b/frontend/src/uxbox/main/data/workspace/selection.cljs @@ -12,16 +12,17 @@ [beicon.core :as rx] [cljs.spec.alpha :as s] [potok.core :as ptk] - [uxbox.main.data.workspace.common :as dwc] - [uxbox.main.worker :as uw] - [uxbox.main.streams :as ms] + [uxbox.common.data :as d] + [uxbox.common.geom.point :as gpt] + [uxbox.common.geom.shapes :as geom] + [uxbox.common.math :as mth] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.common.spec :as us] [uxbox.common.uuid :as uuid] - [uxbox.common.data :as d] - [uxbox.common.geom.shapes :as geom] - [uxbox.common.geom.point :as gpt] - [uxbox.common.math :as mth])) + [uxbox.main.data.workspace.common :as dwc] + [uxbox.main.streams :as ms] + [uxbox.main.worker :as uw])) (s/def ::set-of-uuid (s/every uuid? :kind set?)) @@ -220,7 +221,7 @@ name (generate-unique-name names (:name obj)) renamed-obj (assoc obj :id id :name name) moved-obj (geom/move renamed-obj delta) - frames (cp/select-frames objects) + frames (cph/select-frames objects) frame-id (if frame-id frame-id (dwc/calculate-frame-overlap frames moved-obj)) diff --git a/frontend/src/uxbox/main/data/workspace/transforms.cljs b/frontend/src/uxbox/main/data/workspace/transforms.cljs index 2ad286aae..b08d82765 100644 --- a/frontend/src/uxbox/main/data/workspace/transforms.cljs +++ b/frontend/src/uxbox/main/data/workspace/transforms.cljs @@ -17,6 +17,7 @@ [uxbox.common.data :as d] [uxbox.common.spec :as us] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.main.data.workspace.common :as dwc] [uxbox.main.data.workspace.selection :as dws] [uxbox.main.refs :as refs] @@ -346,7 +347,7 @@ (or recurse-frames? (not (= :frame (:type shape)))))) ;; ID's + Children but remove frame children if the flag is set to false - ids-with-children (concat ids (mapcat #(cp/get-children % objects) + ids-with-children (concat ids (mapcat #(cph/get-children % objects) (filter not-frame-id? ids))) ;; For each shape updates the modifiers given as arguments @@ -391,7 +392,7 @@ (let [objects (get-in state [:workspace-data page-id :objects]) id->obj #(get objects %) - get-children (fn [shape] (map id->obj (cp/get-children (:id shape) objects))) + get-children (fn [shape] (map id->obj (cph/get-children (:id shape) objects))) shapes (concat shapes (mapcat get-children shapes))] (rotate-around-center state delta-rotation center shapes)))))))) @@ -408,7 +409,7 @@ objects (get-in state [:workspace-data page-id :objects]) ;; ID's + Children - ids-with-children (concat ids (mapcat #(cp/get-children % objects) ids)) + ids-with-children (concat ids (mapcat #(cph/get-children % objects) ids)) ;; For each shape applies the modifiers by transforming the objects update-shape diff --git a/frontend/src/uxbox/main/exports.cljs b/frontend/src/uxbox/main/exports.cljs index 6802e84ad..c1b0abfaf 100644 --- a/frontend/src/uxbox/main/exports.cljs +++ b/frontend/src/uxbox/main/exports.cljs @@ -13,6 +13,7 @@ [rumext.alpha :as mf] [uxbox.common.uuid :as uuid] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.common.math :as mth] [uxbox.common.geom.shapes :as geom] [uxbox.common.geom.point :as gpt] @@ -39,7 +40,7 @@ (defn- calculate-dimensions [{:keys [objects] :as data} vport] - (let [shapes (cp/select-toplevel-shapes objects {:include-frames? true})] + (let [shapes (cph/select-toplevel-shapes objects {:include-frames? true})] (->> (geom/selection-rect shapes) (geom/adjust-to-viewport vport) (geom/fix-invalid-rect-values)))) @@ -132,7 +133,7 @@ frame-id (:id frame) - modifier-ids (concat [frame-id] (cp/get-children frame-id objects)) + modifier-ids (concat [frame-id] (cph/get-children frame-id objects)) update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) objects (reduce update-fn objects modifier-ids) frame (assoc-in frame [:modifiers :displacement] modifier) diff --git a/frontend/src/uxbox/main/refs.cljs b/frontend/src/uxbox/main/refs.cljs index ce4454e0d..85dd108c9 100644 --- a/frontend/src/uxbox/main/refs.cljs +++ b/frontend/src/uxbox/main/refs.cljs @@ -10,12 +10,13 @@ (ns uxbox.main.refs "A collection of derived refs." (:require - [okulary.core :as l] [beicon.core :as rx] + [okulary.core :as l] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] + [uxbox.common.uuid :as uuid] [uxbox.main.constants :as c] - [uxbox.main.store :as st] - [uxbox.common.uuid :as uuid])) + [uxbox.main.store :as st])) ;; ---- Global refs @@ -78,7 +79,7 @@ (l/derived :objects workspace-data)) (def workspace-frames - (l/derived cp/select-frames workspace-objects)) + (l/derived cph/select-frames workspace-objects)) (defn object-by-id [id] @@ -106,7 +107,7 @@ objects (get-in state [:workspace-data page-id :objects]) selected (get-in state [:workspace-local :selected]) shape (get objects id) - children (cp/get-children id objects)] + children (cph/get-children id objects)] (some selected children)))] (l/derived selector st/state))) @@ -118,7 +119,7 @@ (let [selected (get-in state [:workspace-local :selected]) page-id (get-in state [:workspace-page :id]) objects (get-in state [:workspace-data page-id :objects]) - children (mapcat #(cp/get-children % objects) selected)] + children (mapcat #(cph/get-children % objects) selected)] (into selected children)))] (l/derived selector st/state))) diff --git a/frontend/src/uxbox/main/ui/viewer/shapes.cljs b/frontend/src/uxbox/main/ui/viewer/shapes.cljs index 8011162eb..f2e0f88bc 100644 --- a/frontend/src/uxbox/main/ui/viewer/shapes.cljs +++ b/frontend/src/uxbox/main/ui/viewer/shapes.cljs @@ -13,6 +13,7 @@ [rumext.alpha :as mf] [uxbox.common.data :as d] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.main.data.viewer :as dv] [uxbox.main.refs :as refs] [uxbox.main.store :as st] @@ -174,7 +175,7 @@ update-fn #(assoc-in %1 [%2 :modifiers :displacement] modifier) frame-id (:id frame) - modifier-ids (d/concat [frame-id] (cp/get-children frame-id objects)) + modifier-ids (d/concat [frame-id] (cph/get-children frame-id objects)) objects (reduce update-fn objects modifier-ids) frame (assoc-in frame [:modifiers :displacement] modifier) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs index a26ade504..6b010a179 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/layers.cljs @@ -16,6 +16,7 @@ [uxbox.common.data :as d] [uxbox.common.uuid :as uuid] [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.main.data.workspace :as dw] [uxbox.main.refs :as refs] [uxbox.main.store :as st] @@ -154,7 +155,7 @@ (if (= side :center) (st/emit! (dw/relocate-shape id (:id item) 0)) (let [to-index (if (= side :top) (inc index) index) - parent-id (cp/get-parent (:id item) objects)] + parent-id (cph/get-parent (:id item) objects)] (st/emit! (dw/relocate-shape id parent-id to-index))))) on-hold @@ -223,7 +224,7 @@ old-obs (unchecked-get oprops "objects")] (and (= new-itm old-itm) (identical? new-idx old-idx) - (let [childs (cp/get-children (:id new-itm) new-obs) + (let [childs (cph/get-children (:id new-itm) new-obs) childs' (conj childs (:id new-itm))] (and (or (= new-sel old-sel) (not (or (boolean (some new-sel childs')) @@ -273,7 +274,7 @@ (defn- strip-objects [objects] - (let [strip-data #(select-keys % [:id :name :blocked :hidden :shapes :type :content :metadata])] + (let [strip-data #(select-keys % [:id :name :blocked :hidden :shapes :type :content :parent-id :metadata])] (persistent! (reduce-kv (fn [res id obj] (assoc! res id (strip-data obj))) diff --git a/frontend/src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs b/frontend/src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs index f389fa7ca..09ae5f85a 100644 --- a/frontend/src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs +++ b/frontend/src/uxbox/main/ui/workspace/sidebar/options/interactions.cljs @@ -11,7 +11,7 @@ (:require [rumext.alpha :as mf] [uxbox.main.ui.icons :as i] - [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] [uxbox.main.data.workspace :as dw] [uxbox.main.refs :as refs] [uxbox.main.store :as st] @@ -31,7 +31,7 @@ destination (get objects (:destination interaction)) frames (mf/use-memo (mf/deps objects) - #(cp/select-frames objects)) + #(cph/select-frames objects)) show-frames-dropdown? (mf/use-state false) diff --git a/frontend/src/uxbox/worker/selection.cljs b/frontend/src/uxbox/worker/selection.cljs index b75dc7f22..06a4af085 100644 --- a/frontend/src/uxbox/worker/selection.cljs +++ b/frontend/src/uxbox/worker/selection.cljs @@ -12,12 +12,13 @@ [cljs.spec.alpha :as s] [okulary.core :as l] [uxbox.common.exceptions :as ex] - [uxbox.common.spec :as us] - [uxbox.common.pages :as cp] - [uxbox.common.uuid :as uuid] - [uxbox.worker.impl :as impl] [uxbox.common.geom.shapes :as geom] - [uxbox.util.quadtree :as qdt])) + [uxbox.common.pages :as cp] + [uxbox.common.pages-helpers :as cph] + [uxbox.common.spec :as us] + [uxbox.common.uuid :as uuid] + [uxbox.util.quadtree :as qdt] + [uxbox.worker.impl :as impl])) (defonce state (l/atom {})) @@ -63,7 +64,7 @@ (defn- create-index [objects] - (let [shapes (->> (cp/select-toplevel-shapes objects {:include-frames? true}) + (let [shapes (->> (cph/select-toplevel-shapes objects {:include-frames? true}) (map #(merge % (select-keys % [:x :y :width :height])))) bounds (geom/selection-rect shapes) bounds #js {:x (:x bounds) diff --git a/frontend/src/uxbox/worker/snaps.cljs b/frontend/src/uxbox/worker/snaps.cljs index 0d41634df..4558750bb 100644 --- a/frontend/src/uxbox/worker/snaps.cljs +++ b/frontend/src/uxbox/worker/snaps.cljs @@ -12,7 +12,8 @@ [okulary.core :as l] [uxbox.common.uuid :as uuid] [uxbox.common.pages :as cp] - [uxbox.common.data :as d] + [uxbox.common.pages-helpers :as cph] + [uxbox.common.data :as d] [uxbox.worker.impl :as impl] [uxbox.util.range-tree :as rt] [uxbox.util.geom.snap-points :as snap] @@ -45,7 +46,7 @@ (let [frame-shapes (->> (vals objects) (filter :frame-id) (group-by :frame-id)) - frame-shapes (->> (cp/select-frames objects) + frame-shapes (->> (cph/select-frames objects) (reduce #(update %1 (:id %2) conj %2) frame-shapes))] (d/mapm (fn [frame-id shapes] {:x (create-coord-data frame-id shapes :x) :y (create-coord-data frame-id shapes :y)})