mirror of
https://github.com/penpot/penpot.git
synced 2025-02-08 08:09:14 -05:00
🐛 Fix inconsistent handing of shape relocation.
This commit is contained in:
parent
1dd1b9d987
commit
df7092e2d4
6 changed files with 126 additions and 57 deletions
|
@ -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])))
|
||||
))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 []
|
||||
|
|
Loading…
Add table
Reference in a new issue