mirror of
https://github.com/penpot/penpot.git
synced 2025-04-11 06:21:30 -05:00
♻️ Add undo/redo.
Reimplement :mov-shape change type operation.
This commit is contained in:
parent
7a5145fa37
commit
63a339dd31
5 changed files with 348 additions and 167 deletions
163
backend/tests/uxbox/tests/test_common_pages.clj
Normal file
163
backend/tests/uxbox/tests/test_common_pages.clj
Normal file
|
@ -0,0 +1,163 @@
|
|||
;; This Source Code Form is subject to the terms of the Mozilla Public
|
||||
;; License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
;;
|
||||
;; Copyright (c) 2019 Andrey Antukh <niwi@niwi.nz>
|
||||
|
||||
(ns uxbox.tests.test-common-pages
|
||||
(:require
|
||||
[clojure.test :as t]
|
||||
[promesa.core :as p]
|
||||
[mockery.core :refer [with-mock]]
|
||||
[uxbox.common.pages :as cp]
|
||||
[uxbox.util.uuid :as uuid]
|
||||
[uxbox.tests.helpers :as th]))
|
||||
|
||||
(t/deftest process-change-add-shape
|
||||
(let [data cp/default-page-data
|
||||
id (uuid/next)
|
||||
chg {:type :add-shape
|
||||
:id id
|
||||
:session-id (uuid/next)
|
||||
:shape {:id id
|
||||
:type :rect
|
||||
:name "rect"}}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 1 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
|
||||
(t/is (= id (get-in res [:shapes 0])))
|
||||
(t/is (= (:shape chg)
|
||||
(get-in res [:shapes-by-id id])))))
|
||||
|
||||
(t/deftest process-change-add-canvas
|
||||
(let [data cp/default-page-data
|
||||
id (uuid/next)
|
||||
chg {:type :add-canvas
|
||||
:id id
|
||||
:session-id (uuid/next)
|
||||
:shape {:id id
|
||||
:type :rect
|
||||
:name "rect"}}
|
||||
res (cp/process-changes data [chg])]
|
||||
(t/is (= 0 (count (:shapes res))))
|
||||
(t/is (= 1 (count (:canvas res))))
|
||||
|
||||
(t/is (= id (get-in res [:canvas 0])))
|
||||
(t/is (= (:shape chg)
|
||||
(get-in res [:shapes-by-id id])))))
|
||||
|
||||
|
||||
(t/deftest process-change-mod-shape
|
||||
(let [id (uuid/next)
|
||||
data (merge cp/default-page-data
|
||||
{:shapes [id]
|
||||
:shapes-by-id {id {:id id
|
||||
:type :rect
|
||||
:name "rect"}}})
|
||||
|
||||
chg {:type :mod-shape
|
||||
:id id
|
||||
:session-id (uuid/next)
|
||||
:operations [[:set :name "foobar"]]}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 1 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
(t/is (= "foobar"
|
||||
(get-in res [:shapes-by-id id :name])))))
|
||||
|
||||
(t/deftest process-change-mod-opts
|
||||
(t/testing "mod-opts add"
|
||||
(let [data cp/default-page-data
|
||||
chg {:type :mod-opts
|
||||
:session-id (uuid/next)
|
||||
:operations [[:set :foo "bar"]]}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 0 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
(t/is (empty? (:shapes-by-id res)))
|
||||
(t/is (= "bar" (get-in res [:options :foo])))))
|
||||
|
||||
(t/testing "mod-opts set nil"
|
||||
(let [data (merge cp/default-page-data
|
||||
{:options {:foo "bar"}})
|
||||
chg {:type :mod-opts
|
||||
:session-id (uuid/next)
|
||||
:operations [[:set :foo nil]]}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 0 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
(t/is (empty? (:shapes-by-id res)))
|
||||
(t/is (not (contains? (:options res) :foo)))))
|
||||
)
|
||||
|
||||
|
||||
(t/deftest process-change-del-shape
|
||||
(let [id (uuid/next)
|
||||
data (merge cp/default-page-data
|
||||
{:shapes [id]
|
||||
:shapes-by-id {id {:id id
|
||||
:type :rect
|
||||
:name "rect"}}})
|
||||
chg {:type :del-shape
|
||||
:id id
|
||||
:session-id (uuid/next)}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 0 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
(t/is (empty? (:shapes-by-id res)))))
|
||||
|
||||
(t/deftest process-change-del-canvas
|
||||
(let [id (uuid/next)
|
||||
data (merge cp/default-page-data
|
||||
{:canvas [id]
|
||||
:shapes-by-id {id {:id id
|
||||
:type :canvas
|
||||
:name "rect"}}})
|
||||
chg {:type :del-canvas
|
||||
:id id
|
||||
:session-id (uuid/next)}
|
||||
res (cp/process-changes data [chg])]
|
||||
|
||||
(t/is (= 0 (count (:shapes res))))
|
||||
(t/is (= 0 (count (:canvas res))))
|
||||
(t/is (empty? (:shapes-by-id res)))))
|
||||
|
||||
|
||||
(t/deftest process-change-mov-shape
|
||||
(let [id1 (uuid/next)
|
||||
id2 (uuid/next)
|
||||
id3 (uuid/next)
|
||||
data (merge cp/default-page-data
|
||||
{:shapes [id1 id2 id3]})]
|
||||
|
||||
(t/testing "mov-canvas 1"
|
||||
(let [chg {:type :mov-shape
|
||||
:id id3
|
||||
:index 0
|
||||
:session-id (uuid/next)}
|
||||
res (cp/process-changes data [chg])]
|
||||
(t/is (= [id3 id1 id2] (:shapes res)))))
|
||||
|
||||
(t/testing "mov-canvas 2"
|
||||
(let [chg {:type :mov-shape
|
||||
:id id3
|
||||
:index 100
|
||||
:session-id (uuid/next)}
|
||||
res (cp/process-changes data [chg])]
|
||||
(t/is (= [id1 id2 id3] (:shapes res)))))
|
||||
|
||||
(t/testing "mov-canvas 3"
|
||||
(let [chg {:type :mov-shape
|
||||
:id id3
|
||||
:index 1
|
||||
:session-id (uuid/next)}
|
||||
res (cp/process-changes data [chg])]
|
||||
(t/is (= [id1 id3 id2] (:shapes res)))))
|
||||
))
|
||||
|
|
@ -53,6 +53,7 @@
|
|||
(s/def ::cy number?)
|
||||
(s/def ::width number?)
|
||||
(s/def ::height number?)
|
||||
(s/def ::index integer?)
|
||||
|
||||
(s/def ::shape-attrs
|
||||
(s/keys :opt-un [::blocked
|
||||
|
@ -65,7 +66,6 @@
|
|||
::font-style
|
||||
::font-weight
|
||||
::hidden
|
||||
;; ::page-id ??
|
||||
::letter-spacing
|
||||
::line-height
|
||||
::locked
|
||||
|
@ -103,7 +103,6 @@
|
|||
|
||||
;; Changes related
|
||||
(s/def ::operation (s/tuple #{:set} keyword? any?))
|
||||
(s/def ::move-after-id (s/nilable uuid?))
|
||||
|
||||
(s/def ::operations
|
||||
(s/coll-of ::operation :kind vector?))
|
||||
|
@ -120,7 +119,7 @@
|
|||
(s/keys :req-un [::id ::operations ::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mov-shape [_]
|
||||
(s/keys :req-un [::id ::move-after-id ::session-id]))
|
||||
(s/keys :req-un [::id ::index ::session-id]))
|
||||
|
||||
(defmethod change-spec-impl :mod-opts [_]
|
||||
(s/keys :req-un [::operations ::session-id]))
|
||||
|
@ -198,20 +197,6 @@
|
|||
% operations))
|
||||
data))
|
||||
|
||||
;; (defn- process-mod-shape
|
||||
;; [data {:keys [id operations] :as change}]
|
||||
;; (if-let [shape (get-in data [:shapes-by-id id])]
|
||||
;; (let [shape (reduce (fn [shape [_ att val]]
|
||||
;; (if (nil? val)
|
||||
;; (dissoc shape att)
|
||||
;; (assoc shape att val)))
|
||||
;; shape
|
||||
;; operations)]
|
||||
;; (if (empty? shape)
|
||||
;; (update data :shapes-by-id dissoc id)
|
||||
;; (update data :shapes-by-id assoc id shape)))
|
||||
;; data))
|
||||
|
||||
(defn- process-mod-opts
|
||||
[data {:keys [operations]}]
|
||||
(update data :options
|
||||
|
@ -222,19 +207,19 @@
|
|||
% operations)))
|
||||
|
||||
(defn- process-mov-shape
|
||||
[data {:keys [id move-after-id]}]
|
||||
[data {:keys [id index]}]
|
||||
(let [shapes (:shapes data)
|
||||
shapes' (into [] (remove #(= % id) shapes))
|
||||
index (d/index-of shapes' move-after-id)]
|
||||
current-index (d/index-of shapes id)
|
||||
shapes' (into [] (remove #(= % id) shapes))]
|
||||
(cond
|
||||
(= id move-after-id)
|
||||
(assoc data :shapes shapes)
|
||||
(= index current-index)
|
||||
data
|
||||
|
||||
(nil? index)
|
||||
(nil? current-index)
|
||||
(assoc data :shapes (d/concat [id] shapes'))
|
||||
|
||||
:else
|
||||
(let [[before after] (split-at (inc index) shapes')]
|
||||
(let [[before after] (split-at index shapes')]
|
||||
(assoc data :shapes (d/concat [] before [id] after))))))
|
||||
|
||||
(defn- process-del-shape
|
||||
|
|
|
@ -165,95 +165,84 @@
|
|||
(ptk/reify ::handle-page-change
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(prn "handle-page-change")
|
||||
(let [page-id' (get-in state [:workspace-page :id])]
|
||||
(when (= page-id page-id')
|
||||
(rx/of (shapes-changes-commited msg)))))))
|
||||
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Undo/Redo
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
;; (def undo-hierarchy
|
||||
;; (-> (make-hierarchy)
|
||||
;; (derive ::update-shape ::undo-signal)
|
||||
;; (derive ::update-options ::undo-signal)
|
||||
;; (derive ::move-selected-layer ::undo-signal)
|
||||
;; (derive ::materialize-temporal-modifier-in-bulk ::undo-signal)
|
||||
;; (derive ::add-shape ::undo-signal)
|
||||
;; (derive ::add-canvas ::undo-signal)))
|
||||
(def MAX-UNDO-SIZE 50)
|
||||
|
||||
;; (def MAX-UNDO-SIZE 50)
|
||||
(defn- conj-undo-entry
|
||||
[undo data]
|
||||
(let [undo (conj undo data)]
|
||||
(if (> (count undo) MAX-UNDO-SIZE)
|
||||
(into [] (take MAX-UNDO-SIZE undo))
|
||||
undo)))
|
||||
|
||||
;; (defn- conj-undo-entry
|
||||
;; [undo data]
|
||||
;; (let [undo (conj undo data)]
|
||||
;; (if (> (count undo) MAX-UNDO-SIZE)
|
||||
;; (into [] (take MAX-UNDO-SIZE undo))
|
||||
;; undo)))
|
||||
(defn- materialize-undo
|
||||
[changes index]
|
||||
(ptk/reify ::materialize-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-data cp/process-changes changes)
|
||||
(assoc-in [:workspace-local :undo-index] index)))))
|
||||
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [pid (get-in state [:workspace-page :id])
|
||||
;; data (:workspace-data state)
|
||||
;; undo (-> (get-in state [:undo pid] [])
|
||||
;; (conj-undo-entry data))]
|
||||
;; (prn "diff-and-commit-changes" "undo=" (count undo))
|
||||
;; (-> state
|
||||
;; (assoc-in [:undo pid] undo)
|
||||
;; (update :workspace-local dissoc :undo-index))))
|
||||
(defn- reset-undo
|
||||
[index]
|
||||
(ptk/reify ::reset-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(update :workspace-local dissoc :undo-index)
|
||||
(update-in [:workspace-local :undo]
|
||||
(fn [queue]
|
||||
(into [] (take (inc index) queue))))))))
|
||||
|
||||
;; (defn initialize-undo
|
||||
;; [page-id]
|
||||
;; (ptk/reify ::initialize-page
|
||||
;; ptk/WatchEvent
|
||||
;; (watch [_ state stream]
|
||||
;; (let [stoper (rx/filter #(or (ptk/type? ::finalize %)
|
||||
;; (ptk/type? ::initialize-page %))
|
||||
;; stream)
|
||||
;; undo-event? #(or (isa? (ptk/type %) ::undo-signal)
|
||||
;; (satisfies? IBatchedChange %))]
|
||||
;; (->> stream
|
||||
;; (rx/filter #(satisfies? IBatchedChange %))
|
||||
;; (rx/debounce 200)
|
||||
;; (rx/map (constantly diff-and-commit-changes))
|
||||
;; (rx/take-until stoper))))))
|
||||
(s/def ::undo-changes ::cp/changes)
|
||||
(s/def ::redo-changes ::cp/changes)
|
||||
(s/def ::undo-entry
|
||||
(s/keys :req-un [::undo-changes ::redo-changes]))
|
||||
|
||||
;; (def undo
|
||||
;; (ptk/reify ::undo
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [pid (get-in state [:workspace-page :id])
|
||||
;; undo (get-in state [:undo pid] [])
|
||||
;; index (get-in state [:workspace-local :undo-index])
|
||||
;; index (or index (dec (count undo)))]
|
||||
;; (if (or (empty? undo) (= index 0))
|
||||
;; state
|
||||
;; (let [index (dec index)]
|
||||
;; (-> state
|
||||
;; (assoc :workspace-data (nth undo index))
|
||||
;; (assoc-in [:workspace-local :undo-index] index))))))))
|
||||
(defn- append-undo
|
||||
[entry]
|
||||
(us/verify ::undo-entry entry)
|
||||
(ptk/reify ::append-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:workspace-local :undo] (fnil conj-undo-entry []) entry))))
|
||||
|
||||
(def undo
|
||||
(ptk/reify ::undo
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [local (:workspace-local state)
|
||||
undo (:undo local [])
|
||||
index (or (:undo-index local)
|
||||
(dec (count undo)))]
|
||||
(when-not (or (empty? undo) (= index -1))
|
||||
(let [changes (get-in undo [index :undo-changes])]
|
||||
(rx/of (materialize-undo changes (dec index))
|
||||
(commit-changes changes [] false))))))))
|
||||
|
||||
(def redo
|
||||
(ptk/reify ::redo
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [local (:workspace-local state)
|
||||
undo (:undo local [])
|
||||
index (or (:undo-index local)
|
||||
(dec (count undo)))]
|
||||
(when-not (or (empty? undo) (= index (dec (count undo))))
|
||||
(let [changes (get-in undo [(inc index) :redo-changes])]
|
||||
(rx/of (materialize-undo changes (inc index))
|
||||
(commit-changes changes [] false))))))))
|
||||
|
||||
;; (def redo
|
||||
;; (ptk/reify ::redo
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (let [pid (get-in state [:workspace-page :id])
|
||||
;; undo (get-in state [:undo pid] [])
|
||||
;; index (get-in state [:workspace-local :undo-index])
|
||||
;; index (or index (dec (count undo)))]
|
||||
;; (if (or (empty? undo) (= index (dec (count undo))))
|
||||
;; state
|
||||
;; (let [index (inc index)]
|
||||
;; (-> state
|
||||
;; (assoc :workspace-data (nth undo index))
|
||||
;; (assoc-in [:workspace-local :undo-index] index))))))))
|
||||
|
||||
;; (def reset-undo-index
|
||||
;; (ptk/reify ::reset-undo-index
|
||||
;; ptk/UpdateEvent
|
||||
;; (update [_ state]
|
||||
;; (update :workspace-local dissoc :undo-index))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Workspace Initialization
|
||||
|
@ -341,9 +330,10 @@
|
|||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [page (get-in state [:pages page-id])
|
||||
data (get-in state [:pages-data page-id])]
|
||||
data (get-in state [:pages-data page-id])
|
||||
local (get-in state [:workspace-cache page-id] workspace-default)]
|
||||
(assoc state
|
||||
:workspace-local workspace-default
|
||||
:workspace-local local
|
||||
:workspace-data data
|
||||
:workspace-page page)))
|
||||
|
||||
|
@ -365,10 +355,24 @@
|
|||
(ptk/reify ::finalize
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
state
|
||||
#_(dissoc state
|
||||
:workspace-page
|
||||
:workspace-data))))
|
||||
(let [local (:workspace-local state)]
|
||||
(assoc-in state [:workspace-cache page-id] local)))))
|
||||
|
||||
(defn- generate-changes
|
||||
[session-id prev curr]
|
||||
(let [diff (d/diff-maps prev curr)]
|
||||
(loop [scs (rest diff)
|
||||
sc (first diff)
|
||||
res []]
|
||||
(if (nil? sc)
|
||||
res
|
||||
(let [[_ id shape] sc]
|
||||
(recur (rest scs)
|
||||
(first scs)
|
||||
(conj res {:type :mod-shape
|
||||
:session-id session-id
|
||||
:operations (d/diff-maps (get prev id) shape)
|
||||
:id id})))))))
|
||||
|
||||
(def diff-and-commit-changes
|
||||
(ptk/reify ::diff-and-commit-changes
|
||||
|
@ -377,23 +381,11 @@
|
|||
(let [pid (get-in state [:workspace-page :id])
|
||||
curr (get-in state [:workspace-data :shapes-by-id])
|
||||
prev (get-in state [:pages-data pid :shapes-by-id])
|
||||
|
||||
diff (d/diff-maps prev curr)
|
||||
changes (loop [scs (rest diff)
|
||||
sc (first diff)
|
||||
res []]
|
||||
(if (nil? sc)
|
||||
res
|
||||
(let [[_ id shape] sc]
|
||||
(recur (rest scs)
|
||||
(first scs)
|
||||
(conj res {:type :mod-shape
|
||||
:session-id (:session-id state)
|
||||
:operations (d/diff-maps (get prev id) shape)
|
||||
:id id})))))]
|
||||
session-id (:session-id state)
|
||||
changes (generate-changes session-id prev curr)
|
||||
undo-changes (generate-changes session-id curr prev)]
|
||||
(when-not (empty? changes)
|
||||
(rx/of (commit-changes changes)))))))
|
||||
|
||||
(rx/of (commit-changes changes undo-changes)))))))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; Data Fetching & Uploading
|
||||
|
@ -924,6 +916,9 @@
|
|||
(rx/of (commit-changes [{:type :add-shape
|
||||
:session-id sid
|
||||
:shape shape
|
||||
:id id}]
|
||||
[{:type :del-shape
|
||||
:session-id sid
|
||||
:id id}])
|
||||
(select-shape id)))))))
|
||||
|
||||
|
@ -952,6 +947,9 @@
|
|||
(rx/of (commit-changes [{:type :add-canvas
|
||||
:session-id sid
|
||||
:shape shape
|
||||
:id id}]
|
||||
[{:type :del-canvas
|
||||
:session-id sid
|
||||
:id id}])))))))
|
||||
|
||||
|
||||
|
@ -975,10 +973,15 @@
|
|||
:id (:id shape)
|
||||
:shape shape
|
||||
:session-id sid})
|
||||
shapes)
|
||||
uchanges (mapv (fn [shape]
|
||||
{:type :del-shape
|
||||
:id (:id shape)
|
||||
:session-id sid})
|
||||
shapes)]
|
||||
(rx/merge
|
||||
(rx/from (map (fn [s] #(impl-assoc-shape % s)) shapes))
|
||||
(rx/of (commit-changes changes)))))))
|
||||
(rx/of (commit-changes changes uchanges)))))))
|
||||
|
||||
;; --- Toggle shape's selection status (selected or deselected)
|
||||
|
||||
|
@ -1151,11 +1154,18 @@
|
|||
(map (fn [{:keys [type id] :as shape}]
|
||||
{:type (if (= type :canvas) :del-canvas :del-shape)
|
||||
:session-id session-id
|
||||
:id id})))]
|
||||
:id id})))
|
||||
uchanges (->> selected
|
||||
(map lookup-shape)
|
||||
(map (fn [{:keys [type id] :as shape}]
|
||||
{:type (if (= type :canvas) :add-canvas :add-shape)
|
||||
:session-id session-id
|
||||
:shape shape
|
||||
:id id})))]
|
||||
(rx/concat
|
||||
(rx/of deselect-all)
|
||||
(rx/from (map impl-dissoc-shape selected))
|
||||
(rx/of (commit-changes changes)))))))
|
||||
(rx/of (commit-changes changes uchanges)))))))
|
||||
|
||||
;; --- Rename Shape
|
||||
|
||||
|
@ -1164,18 +1174,19 @@
|
|||
(us/verify ::us/uuid id)
|
||||
(us/verify string? name)
|
||||
(ptk/reify ::rename-shape
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:shapes id :name] name))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [session-id (:session-id state)
|
||||
change {:type :mod-shape
|
||||
:id id
|
||||
:session-id session-id
|
||||
:operations [[:set :name name]]}]
|
||||
(rx/of (commit-changes [change]))))))
|
||||
(let [shape (get-in state [:workspace-data :shapes-by-id id])
|
||||
session-id (:session-id state)
|
||||
change {:type :mod-shape
|
||||
:id id
|
||||
:session-id session-id
|
||||
:operations [[:set :name name]]}
|
||||
uchange {:type :mod-shape
|
||||
:id id
|
||||
:session-id session-id
|
||||
:operations [[:set :name (:name shape)]]}]
|
||||
(rx/of (commit-changes [change] [uchange]))))))
|
||||
|
||||
;; --- Shape Vertical Ordering
|
||||
|
||||
|
@ -1211,7 +1222,7 @@
|
|||
|
||||
;; --- Change Shape Order (D&D Ordering)
|
||||
|
||||
(defn temporal-shape-order-change
|
||||
(defn shape-order-change
|
||||
[id index]
|
||||
(us/verify ::us/uuid id)
|
||||
(us/verify number? index)
|
||||
|
@ -1221,22 +1232,31 @@
|
|||
(let [shapes (get-in state [:workspace-data :shapes])
|
||||
shapes (into [] (remove #(= % id)) shapes)
|
||||
[before after] (split-at index shapes)
|
||||
shapes (d/concat [] before [id] after)
|
||||
change {:type :mov-shape
|
||||
:session-id (:session-id state)
|
||||
:move-after-id (last before)
|
||||
:id id}]
|
||||
(-> state
|
||||
(assoc-in [:workspace-data :shapes] shapes)
|
||||
(assoc ::tmp-shape-order-change change))))))
|
||||
shapes (d/concat [] before [id] after)]
|
||||
(assoc-in state [:workspace-data :shapes] shapes)))))
|
||||
|
||||
(def commit-shape-order-change
|
||||
(defn commit-shape-order-change
|
||||
[id]
|
||||
(ptk/reify ::commit-shape-order-change
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [change (::tmp-shape-order-change state)]
|
||||
(rx/of #(dissoc state ::tmp-shape-order-change)
|
||||
(commit-changes [change]))))))
|
||||
(let [page-id (get-in state [:workspace-page :id])
|
||||
curr-shapes (get-in state [:workspace-data :shapes])
|
||||
prev-shapes (get-in state [:pages-data page-id :shapes])
|
||||
|
||||
curr-index (d/index-of curr-shapes id)
|
||||
prev-index (d/index-of prev-shapes id)
|
||||
session-id (:session-id state)
|
||||
|
||||
change {:type :mov-shape
|
||||
:session-id session-id
|
||||
:id id
|
||||
:index curr-index}
|
||||
uchange {:type :mov-shape
|
||||
:session-id session-id
|
||||
:id id
|
||||
:index prev-index}]
|
||||
(rx/of (commit-changes [change] [uchange]))))))
|
||||
|
||||
;; --- Change Canvas Order (D&D Ordering)
|
||||
|
||||
|
@ -1326,23 +1346,35 @@
|
|||
(reduce process-shape state ids)))))
|
||||
|
||||
(defn commit-changes
|
||||
[changes]
|
||||
(us/verify ::cp/changes changes)
|
||||
(ptk/reify ::commit-changes
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [pid (get-in state [:workspace-page :id])
|
||||
data (get-in state [:pages-data pid])]
|
||||
(update-in state [:pages-data pid] cp/process-changes changes)))
|
||||
([changes undo-changes] (commit-changes changes undo-changes true))
|
||||
([changes undo-changes save-undo?]
|
||||
(us/verify ::cp/changes changes)
|
||||
(us/verify ::cp/changes undo-changes)
|
||||
(ptk/reify ::commit-changes
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [pid (get-in state [:workspace-page :id])
|
||||
data (get-in state [:pages-data pid])]
|
||||
(update-in state [:pages-data pid] cp/process-changes changes)))
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page (:workspace-page state)
|
||||
params {:id (:id page)
|
||||
:revn (:revn page)
|
||||
:changes (vec changes)}]
|
||||
(->> (rp/mutation :update-page params)
|
||||
(rx/map shapes-changes-commited))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [page (:workspace-page state)
|
||||
uidx (get-in state [:workspace-local :undo-index] ::not-found)
|
||||
params {:id (:id page)
|
||||
:revn (:revn page)
|
||||
:changes (vec changes)}]
|
||||
(rx/concat
|
||||
(when (and save-undo? (not= uidx ::not-found))
|
||||
(rx/of (reset-undo uidx)))
|
||||
|
||||
(when save-undo?
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes changes}]
|
||||
(rx/of (append-undo entry))))
|
||||
|
||||
(->> (rp/mutation :update-page params)
|
||||
(rx/map shapes-changes-commited))))))))
|
||||
|
||||
(s/def ::shapes-changes-commited
|
||||
(s/keys :req-un [::page-id ::revn ::cp/changes]))
|
||||
|
|
|
@ -31,9 +31,9 @@
|
|||
:ctrl+0 #(st/emit! (dw/reset-zoom))
|
||||
;; :ctrl+r #(st/emit! (dw/toggle-flag :ruler))
|
||||
:ctrl+d #(st/emit! dw/duplicate-selected)
|
||||
;; :ctrl+z #(st/emit! dw/undo)
|
||||
;; :ctrl+shift+z #(st/emit! dw/redo)
|
||||
;; :ctrl+y #(st/emit! du/redo)
|
||||
:ctrl+z #(st/emit! dw/undo)
|
||||
:ctrl+shift+z #(st/emit! dw/redo)
|
||||
:ctrl+y #(st/emit! dw/redo)
|
||||
:ctrl+b #(st/emit! (dw/select-for-drawing :rect))
|
||||
:ctrl+e #(st/emit! (dw/select-for-drawing :circle))
|
||||
:ctrl+t #(st/emit! (dw/select-for-drawing :text))
|
||||
|
|
|
@ -118,11 +118,12 @@
|
|||
|
||||
on-drop
|
||||
(fn [item monitor]
|
||||
(st/emit! dw/commit-shape-order-change))
|
||||
(prn "index" index)
|
||||
(st/emit! (dw/commit-shape-order-change (:shape-id item))))
|
||||
|
||||
on-hover
|
||||
(fn [item monitor]
|
||||
(st/emit! (dw/temporal-shape-order-change (:shape-id item) index)))
|
||||
(st/emit! (dw/shape-order-change (:shape-id item) index)))
|
||||
|
||||
[dprops dnd-ref] (use-sortable
|
||||
{:type "layer-item"
|
||||
|
|
Loading…
Add table
Reference in a new issue