mirror of
https://github.com/penpot/penpot.git
synced 2026-04-25 11:18:36 +00:00
✨ Add the ability for save and restore selection state in undo/redo (#8652)
* ✨ Capture selection state before changes are applied Save current selection IDs in commit-changes so undo entries can track what was selected before each action. * ✨ Save and restore selection state in undo/redo Extend undo entry with selected-before and selected-after fields. On undo, restore selection to what it was before the action. On redo, restore selection to what it was after the action. Handles single entries, stacked entries, accumulated transactions, and undo groups. Fixes #6007 * ♻️ Wire selected-before through workspace undo stream Pass the captured selection state from commit data into the undo entry so it is stored alongside changes. * 🐛 Fix unmatched delimiter in changes.cljs * 🐛 Pass selected-before through commit event to undo entry selected-before was captured in commit-changes but dropped by the commit function since it was missing from the destructuring and the commit map. This caused restore-selection to receive nil on undo. --------- Signed-off-by: eureka928 <meobius123@gmail.com> Co-authored-by: Mihai <noreply@github.com>
This commit is contained in:
parent
48e8c0bc65
commit
0c08dfb13d
@ -122,7 +122,8 @@
|
||||
(defn commit
|
||||
"Create a commit event instance"
|
||||
[{:keys [commit-id redo-changes undo-changes origin save-undo? features
|
||||
file-id file-revn file-vern undo-group tags stack-undo? source ignore-wasm?]}]
|
||||
file-id file-revn file-vern undo-group tags stack-undo? source ignore-wasm?
|
||||
selected-before]}]
|
||||
|
||||
(assert (cpc/check-changes redo-changes)
|
||||
"expect valid vector of changes for redo-changes")
|
||||
@ -148,7 +149,8 @@
|
||||
:undo-group undo-group
|
||||
:tags tags
|
||||
:stack-undo? stack-undo?
|
||||
:ignore-wasm? ignore-wasm?}]
|
||||
:ignore-wasm? ignore-wasm?
|
||||
:selected-before selected-before}]
|
||||
|
||||
(ptk/reify ::commit
|
||||
cljs.core/IDeref
|
||||
@ -205,15 +207,17 @@
|
||||
|
||||
;; Prevent commit changes by a viewer team member (it really should never happen)
|
||||
(when (:can-edit permissions)
|
||||
(rx/of (-> params
|
||||
(assoc :undo-group undo-group)
|
||||
(assoc :features features)
|
||||
(assoc :tags tags)
|
||||
(assoc :stack-undo? stack-undo?)
|
||||
(assoc :save-undo? save-undo?)
|
||||
(assoc :file-id file-id)
|
||||
(assoc :file-revn (resolve-file-revn state file-id))
|
||||
(assoc :file-vern (resolve-file-vern state file-id))
|
||||
(assoc :undo-changes uchg)
|
||||
(assoc :redo-changes rchg)
|
||||
(commit))))))))
|
||||
(let [selected (dm/get-in state [:workspace-local :selected])]
|
||||
(rx/of (-> params
|
||||
(assoc :undo-group undo-group)
|
||||
(assoc :features features)
|
||||
(assoc :tags tags)
|
||||
(assoc :stack-undo? stack-undo?)
|
||||
(assoc :save-undo? save-undo?)
|
||||
(assoc :file-id file-id)
|
||||
(assoc :file-revn (resolve-file-revn state file-id))
|
||||
(assoc :file-vern (resolve-file-vern state file-id))
|
||||
(assoc :undo-changes uchg)
|
||||
(assoc :redo-changes rchg)
|
||||
(assoc :selected-before selected)
|
||||
(commit)))))))))
|
||||
|
||||
@ -484,12 +484,13 @@
|
||||
(rx/filter dch/commit?)
|
||||
(rx/map deref)
|
||||
(rx/mapcat
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo?]}]
|
||||
(fn [{:keys [save-undo? undo-changes redo-changes undo-group tags stack-undo? selected-before]}]
|
||||
(if (and save-undo? (seq undo-changes))
|
||||
(let [entry {:undo-changes undo-changes
|
||||
:redo-changes redo-changes
|
||||
:undo-group undo-group
|
||||
:tags tags}]
|
||||
:tags tags
|
||||
:selected-before selected-before}]
|
||||
(rx/of (dwu/append-undo entry stack-undo?)))
|
||||
(rx/empty))))))
|
||||
(rx/take-until stoper-s))))
|
||||
|
||||
@ -60,7 +60,9 @@
|
||||
[:undo-changes [:vector cpc/schema:change]]
|
||||
[:redo-changes [:vector cpc/schema:change]]
|
||||
[:undo-group ::sm/uuid]
|
||||
[:tags [:set :keyword]]])
|
||||
[:tags [:set :keyword]]
|
||||
[:selected-before {:optional true} [:maybe [:set ::sm/uuid]]]
|
||||
[:selected-after {:optional true} [:maybe [:set ::sm/uuid]]]])
|
||||
|
||||
(def check-undo-entry
|
||||
(sm/check-fn schema:undo-entry))
|
||||
@ -103,24 +105,28 @@
|
||||
(defn- stack-undo-entry
|
||||
"Extends the current undo entry in the workspace with new changes if it
|
||||
exists, or creates a new entry if it doesn't."
|
||||
[state {:keys [undo-changes redo-changes] :as entry}]
|
||||
[state {:keys [undo-changes redo-changes selected-after] :as entry}]
|
||||
(let [index (get-in state [:workspace-undo :index] -1)]
|
||||
(if (>= index 0)
|
||||
(update-in state [:workspace-undo :items index]
|
||||
(fn [item]
|
||||
(-> item
|
||||
(update :undo-changes #(into undo-changes %))
|
||||
(update :redo-changes #(into % redo-changes)))))
|
||||
(update :redo-changes #(into % redo-changes))
|
||||
(assoc :selected-after selected-after))))
|
||||
(add-undo-entry state entry))))
|
||||
|
||||
(defn- accumulate-undo-entry
|
||||
"Extends the current undo transaction with new changes."
|
||||
[state {:keys [undo-changes redo-changes undo-group tags]}]
|
||||
[state {:keys [undo-changes redo-changes undo-group tags selected-before selected-after]}]
|
||||
(-> state
|
||||
(update-in [:workspace-undo :transaction :undo-changes] #(into undo-changes %))
|
||||
(update-in [:workspace-undo :transaction :redo-changes] #(into % redo-changes))
|
||||
(cond-> (nil? (get-in state [:workspace-undo :transaction :undo-group]))
|
||||
(assoc-in [:workspace-undo :transaction :undo-group] undo-group))
|
||||
(cond-> (nil? (get-in state [:workspace-undo :transaction :selected-before]))
|
||||
(assoc-in [:workspace-undo :transaction :selected-before] selected-before))
|
||||
(assoc-in [:workspace-undo :transaction :selected-after] selected-after)
|
||||
(assoc-in [:workspace-undo :transaction :tags] tags)))
|
||||
|
||||
(defn append-undo
|
||||
@ -137,18 +143,20 @@
|
||||
(ptk/reify ::append-undo
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(cond
|
||||
(and (get-in state [:workspace-undo :transaction])
|
||||
(or (not stack?)
|
||||
(d/not-empty? (get-in state [:workspace-undo :transaction :undo-changes]))
|
||||
(d/not-empty? (get-in state [:workspace-undo :transaction :redo-changes]))))
|
||||
(accumulate-undo-entry state entry)
|
||||
(let [selected-after (dm/get-in state [:workspace-local :selected])
|
||||
entry (assoc entry :selected-after selected-after)]
|
||||
(cond
|
||||
(and (get-in state [:workspace-undo :transaction])
|
||||
(or (not stack?)
|
||||
(d/not-empty? (get-in state [:workspace-undo :transaction :undo-changes]))
|
||||
(d/not-empty? (get-in state [:workspace-undo :transaction :redo-changes]))))
|
||||
(accumulate-undo-entry state entry)
|
||||
|
||||
stack?
|
||||
(stack-undo-entry state entry)
|
||||
stack?
|
||||
(stack-undo-entry state entry)
|
||||
|
||||
:else
|
||||
(add-undo-entry state entry)))))
|
||||
:else
|
||||
(add-undo-entry state entry))))))
|
||||
|
||||
(def empty-tx
|
||||
{:undo-changes [] :redo-changes []})
|
||||
@ -234,6 +242,16 @@
|
||||
(rx/map first)
|
||||
(rx/map commit-undo-transaction))))))
|
||||
|
||||
(defn- restore-selection
|
||||
"Restores the selection state from an undo entry."
|
||||
[selected-ids]
|
||||
(ptk/reify ::restore-selection
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(if (some? selected-ids)
|
||||
(assoc-in state [:workspace-local :selected] selected-ids)
|
||||
state))))
|
||||
|
||||
(defn undo-to-index
|
||||
"Repeat undoing or redoing until dest-index is reached."
|
||||
[dest-index]
|
||||
@ -302,12 +320,15 @@
|
||||
(find-first-group-idx index))]
|
||||
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index (dec undo-group-index)))
|
||||
(let [first-item (get items undo-group-index)]
|
||||
(rx/of (undo-to-index (dec undo-group-index))
|
||||
(restore-selection (:selected-before first-item))))
|
||||
(rx/of (materialize-undo changes (dec index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:save-undo? false
|
||||
:origin it})
|
||||
(restore-selection (:selected-before item))
|
||||
(assure-valid-current-page)))))))))))
|
||||
|
||||
(def redo
|
||||
@ -337,12 +358,15 @@
|
||||
redo-group-index (when undo-group
|
||||
(find-last-group-idx (inc index)))]
|
||||
(if undo-group
|
||||
(rx/of (undo-to-index redo-group-index))
|
||||
(let [last-item (get items redo-group-index)]
|
||||
(rx/of (undo-to-index redo-group-index)
|
||||
(restore-selection (:selected-after last-item))))
|
||||
(rx/of (materialize-undo changes (inc index))
|
||||
(dch/commit-changes {:redo-changes changes
|
||||
:undo-changes []
|
||||
:origin it
|
||||
:save-undo? false})))))))))))
|
||||
:save-undo? false})
|
||||
(restore-selection (:selected-after item))))))))))))
|
||||
|
||||
(defn- assure-valid-current-page
|
||||
[]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user