mirror of
https://github.com/penpot/penpot.git
synced 2025-02-24 07:46:13 -05:00
♻️ Refactor duplicate objects
This commit is contained in:
parent
9c895cb8bb
commit
c626b1d106
4 changed files with 136 additions and 135 deletions
|
@ -32,18 +32,23 @@
|
|||
(vary-meta changes assoc ::objects objects))
|
||||
|
||||
(defn add-obj
|
||||
([changes obj index]
|
||||
(add-obj changes (assoc obj ::index index)))
|
||||
|
||||
([changes obj]
|
||||
(let [add-change
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:page-id (::page-id (meta changes))
|
||||
:parent-id (:parent-id obj)
|
||||
:frame-id (:frame-id obj)
|
||||
:index (::index obj)
|
||||
:obj (dissoc obj ::index :parent-id)}
|
||||
(add-obj changes obj nil))
|
||||
|
||||
([changes obj {:keys [index ignore-touched] :or {index ::undefined ignore-touched false}}]
|
||||
(let [obj (cond-> obj
|
||||
(not= index ::undefined)
|
||||
(assoc :index index))
|
||||
|
||||
add-change
|
||||
{:type :add-obj
|
||||
:id (:id obj)
|
||||
:page-id (::page-id (meta changes))
|
||||
:parent-id (:parent-id obj)
|
||||
:frame-id (:frame-id obj)
|
||||
:index (::index obj)
|
||||
:ignore-touched ignore-touched
|
||||
:obj (dissoc obj ::index :parent-id)}
|
||||
|
||||
del-change
|
||||
{:type :del-obj
|
||||
|
@ -201,3 +206,22 @@
|
|||
:page-id page-id
|
||||
:option option-key
|
||||
:value old-val}))))
|
||||
|
||||
(defn reg-objects
|
||||
[chdata shape-ids]
|
||||
(let [page-id (::page-id (meta chdata))]
|
||||
(-> chdata
|
||||
(update :redo-changes conj {:type :reg-objects :page-id page-id :shapes shape-ids}))))
|
||||
;; No need to do anything to undo
|
||||
|
||||
(defn amend-last-change
|
||||
"Modify the last redo-changes added with an update function."
|
||||
[chdata f]
|
||||
(update chdata :redo-changes
|
||||
#(conj (pop %) (f (peek %)))))
|
||||
|
||||
(defn amend-changes
|
||||
"Modify all redo-changes with an update function."
|
||||
[chdata f]
|
||||
(update chdata :redo-changes #(mapv f %)))
|
||||
|
||||
|
|
|
@ -1718,7 +1718,8 @@
|
|||
;; Analyze the rchange and replace staled media and
|
||||
;; references to the new uploaded media-objects.
|
||||
(process-rchange [media-idx item]
|
||||
(if (= :image (get-in item [:obj :type]))
|
||||
(if (and (= (:type item) :add-obj)
|
||||
(= :image (get-in item [:obj :type])))
|
||||
(update-in item [:obj :metadata]
|
||||
(fn [{:keys [id] :as mdata}]
|
||||
(if-let [mobj (get media-idx id)]
|
||||
|
@ -1818,51 +1819,44 @@
|
|||
;; Calculate position for the pasted elements
|
||||
[frame-id parent-id delta index] (calculate-paste-position state mouse-pos in-viewport?)
|
||||
|
||||
paste-objects (->> paste-objects
|
||||
(d/mapm (fn [_ shape]
|
||||
(-> shape
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :parent-id parent-id)
|
||||
paste-objects (->> paste-objects
|
||||
(d/mapm (fn [_ shape]
|
||||
(-> shape
|
||||
(assoc :frame-id frame-id)
|
||||
(assoc :parent-id parent-id)
|
||||
|
||||
(cond->
|
||||
;; if foreign instance, detach the shape
|
||||
(foreign-instance? shape paste-objects state)
|
||||
(dissoc :component-id
|
||||
:component-file
|
||||
:component-root?
|
||||
:remote-synced?
|
||||
:shape-ref
|
||||
:touched))))))
|
||||
(cond->
|
||||
;; if foreign instance, detach the shape
|
||||
(foreign-instance? shape paste-objects state)
|
||||
(dissoc :component-id
|
||||
:component-file
|
||||
:component-root?
|
||||
:remote-synced?
|
||||
:shape-ref
|
||||
:touched))))))
|
||||
|
||||
all-objects (merge page-objects paste-objects)
|
||||
all-objects (merge page-objects paste-objects)
|
||||
|
||||
page-id (:current-page-id state)
|
||||
unames (-> (wsh/lookup-page-objects state page-id)
|
||||
(dwc/retrieve-used-names)) ;; TODO: move this calculation inside prepare-duplicate-changes?
|
||||
|
||||
rchanges (->> (dws/prepare-duplicate-changes all-objects page-id unames selected delta)
|
||||
(mapv (partial process-rchange media-idx))
|
||||
(mapv (partial change-add-obj-index paste-objects selected index)))
|
||||
|
||||
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
|
||||
(reverse rchanges))
|
||||
page-id (:current-page-id state)
|
||||
changes (-> (dws/prepare-duplicate-changes page-objects all-objects page-id selected delta it)
|
||||
(pcb/amend-changes (partial process-rchange media-idx))
|
||||
(pcb/amend-changes (partial change-add-obj-index paste-objects selected index)))
|
||||
|
||||
;; Adds a reg-objects operation so the groups are updated. We add all the new objects
|
||||
new-objects-ids (->> rchanges (filter #(= (:type %) :add-obj)) (mapv :id))
|
||||
new-objects-ids (->> changes :redo-changes (filter #(= (:type %) :add-obj)) (mapv :id))
|
||||
|
||||
rchanges (conj rchanges {:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes new-objects-ids})
|
||||
changes (pcb/reg-objects changes new-objects-ids)
|
||||
|
||||
selected (->> rchanges
|
||||
selected (->> changes
|
||||
:redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(filter #(selected (:old-id %)))
|
||||
(map #(get-in % [:obj :id]))
|
||||
(into (d/ordered-set)))]
|
||||
|
||||
(rx/of (dch/commit-changes {:redo-changes rchanges
|
||||
:undo-changes uchanges
|
||||
:origin it})
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwc/select-shapes selected))))]
|
||||
|
||||
(ptk/reify ::paste-shape
|
||||
ptk/WatchEvent
|
||||
(watch [it state _]
|
||||
|
|
|
@ -99,7 +99,7 @@
|
|||
shape-id (:id boolean-data)
|
||||
changes (-> (cb/empty-changes it page-id)
|
||||
(cb/with-objects objects)
|
||||
(cb/add-obj boolean-data index)
|
||||
(cb/add-obj boolean-data {:index index})
|
||||
(cb/change-parent shape-id shapes))]
|
||||
(rx/of (dch/commit-changes changes)
|
||||
(dwc/select-shapes (d/ordered-set shape-id)))))))))
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.changes-builder :as pcb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.spec :as us]
|
||||
[app.common.spec.interactions :as cti]
|
||||
|
@ -265,41 +266,32 @@
|
|||
(declare prepare-duplicate-shape-change)
|
||||
|
||||
(defn prepare-duplicate-changes
|
||||
"Prepare objects to paste: generate new id, give them unique names,
|
||||
move to the position of mouse pointer, and find in what frame they
|
||||
fit."
|
||||
[objects page-id unames ids delta]
|
||||
(let [unames (volatile! unames)
|
||||
"Prepare objects to duplicate: generate new id, give them unique names,
|
||||
move to the desired position, and recalculate parents and frames as needed."
|
||||
[page-objects all-objects page-id ids delta it]
|
||||
(let [unames (volatile! (dwc/retrieve-used-names page-objects))
|
||||
update-unames! (fn [new-name] (vswap! unames conj new-name))
|
||||
all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids objects %2))) #{} ids)
|
||||
all-ids (reduce #(into %1 (cons %2 (cph/get-children-ids all-objects %2))) #{} ids)
|
||||
ids-map (into {} (map #(vector % (uuid/next))) all-ids)]
|
||||
(loop [ids (seq ids)
|
||||
chgs []]
|
||||
(if ids
|
||||
(let [id (first ids)
|
||||
result (prepare-duplicate-change objects page-id unames update-unames! ids-map id delta)
|
||||
result (if (vector? result) result [result])]
|
||||
(recur
|
||||
(next ids)
|
||||
(into chgs result)))
|
||||
chgs))))
|
||||
(reduce (fn [changes id]
|
||||
(prepare-duplicate-change changes all-objects page-id unames update-unames! ids-map id delta))
|
||||
(-> (pcb/empty-changes it page-id)
|
||||
(pcb/with-objects all-objects))
|
||||
ids)))
|
||||
|
||||
(defn- prepare-duplicate-change
|
||||
[objects page-id unames update-unames! ids-map id delta]
|
||||
[changes objects page-id unames update-unames! ids-map id delta]
|
||||
(let [obj (get objects id)]
|
||||
(if (cph/frame-shape? obj)
|
||||
(prepare-duplicate-frame-change objects page-id unames update-unames! ids-map obj delta)
|
||||
(prepare-duplicate-shape-change objects page-id unames update-unames! ids-map obj delta (:frame-id obj) (:parent-id obj)))))
|
||||
(prepare-duplicate-frame-change changes objects page-id unames update-unames! ids-map obj delta)
|
||||
(prepare-duplicate-shape-change changes objects page-id unames update-unames! ids-map obj delta (:frame-id obj) (:parent-id obj)))))
|
||||
|
||||
(defn- prepare-duplicate-frame-change
|
||||
[objects page-id unames update-unames! ids-map obj delta]
|
||||
[changes objects page-id unames update-unames! ids-map obj delta]
|
||||
(let [new-id (ids-map (:id obj))
|
||||
frame-name (dwc/generate-unique-name @unames (:name obj))
|
||||
_ (update-unames! frame-name)
|
||||
|
||||
sch (->> (map #(get objects %) (:shapes obj))
|
||||
(mapcat #(prepare-duplicate-shape-change objects page-id unames update-unames! ids-map % delta new-id new-id)))
|
||||
|
||||
new-frame (-> obj
|
||||
(assoc :id new-id
|
||||
:name frame-name
|
||||
|
@ -308,18 +300,27 @@
|
|||
(geom/move delta)
|
||||
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
|
||||
|
||||
fch {:type :add-obj
|
||||
:old-id (:id obj)
|
||||
:page-id page-id
|
||||
:id new-id
|
||||
:frame-id uuid/zero
|
||||
:obj new-frame}]
|
||||
changes (-> (pcb/add-obj changes new-frame)
|
||||
(pcb/amend-last-change #(assoc % :old-id (:id obj))))
|
||||
|
||||
(into [fch] sch)))
|
||||
changes (reduce (fn [changes child]
|
||||
(prepare-duplicate-shape-change changes
|
||||
objects
|
||||
page-id
|
||||
unames
|
||||
update-unames!
|
||||
ids-map
|
||||
child
|
||||
delta
|
||||
new-id
|
||||
new-id))
|
||||
changes
|
||||
(map (d/getf objects) (:shapes obj)))]
|
||||
changes))
|
||||
|
||||
(defn- prepare-duplicate-shape-change
|
||||
[objects page-id unames update-unames! ids-map obj delta frame-id parent-id]
|
||||
(when (some? obj)
|
||||
[changes objects page-id unames update-unames! ids-map obj delta frame-id parent-id]
|
||||
(if (some? obj)
|
||||
(let [new-id (ids-map (:id obj))
|
||||
parent-id (or parent-id frame-id)
|
||||
name (dwc/generate-unique-name @unames (:name obj))
|
||||
|
@ -328,55 +329,44 @@
|
|||
new-obj (-> obj
|
||||
(assoc :id new-id
|
||||
:name name
|
||||
:parent-id parent-id
|
||||
:frame-id frame-id)
|
||||
(dissoc :shapes)
|
||||
(geom/move delta)
|
||||
(d/update-when :interactions #(cti/remap-interactions % ids-map objects)))
|
||||
|
||||
children-changes
|
||||
(loop [result []
|
||||
cid (first (:shapes obj))
|
||||
cids (rest (:shapes obj))]
|
||||
(if (nil? cid)
|
||||
result
|
||||
(let [obj (get objects cid)
|
||||
changes (prepare-duplicate-shape-change objects page-id unames update-unames! ids-map obj delta frame-id new-id)]
|
||||
(recur
|
||||
(into result changes)
|
||||
(first cids)
|
||||
(rest cids)))))]
|
||||
changes (pcb/add-obj changes new-obj {:ignore-touched true})
|
||||
changes (-> (pcb/add-obj changes new-obj {:ignore-touched true})
|
||||
(pcb/amend-last-change #(assoc % :old-id (:id obj))))
|
||||
|
||||
(into [{:type :add-obj
|
||||
:id new-id
|
||||
:page-id page-id
|
||||
:old-id (:id obj)
|
||||
:frame-id frame-id
|
||||
:parent-id parent-id
|
||||
:ignore-touched true
|
||||
:obj new-obj}]
|
||||
children-changes))))
|
||||
|
||||
(declare update-indices)
|
||||
changes (reduce (fn [changes child]
|
||||
(prepare-duplicate-shape-change changes
|
||||
objects
|
||||
page-id
|
||||
unames
|
||||
update-unames!
|
||||
ids-map
|
||||
child
|
||||
delta
|
||||
frame-id
|
||||
new-id))
|
||||
changes
|
||||
(map (d/getf objects) (:shapes obj)))]
|
||||
changes)))
|
||||
|
||||
(defn duplicate-changes-update-indices
|
||||
"Parses the change set when duplicating to set-up the appropriate indices"
|
||||
"Updates the changes to correctly set the indexes of the duplicated objects,
|
||||
depending on the index of the original object respect their parent."
|
||||
[objects ids changes]
|
||||
(let [;; index-map is a map that goes from parent-id => vector([id index-in-parent])
|
||||
index-map (reduce (fn [index-map id]
|
||||
(let [parent-id (get-in objects [id :parent-id])
|
||||
parent-index (cph/get-position-on-parent objects id)]
|
||||
(update index-map parent-id (fnil conj []) [id parent-index])))
|
||||
{}
|
||||
ids)
|
||||
|
||||
(let [process-id
|
||||
(fn [index-map id]
|
||||
(let [parent-id (get-in objects [id :parent-id])
|
||||
parent-index (cph/get-position-on-parent objects id)]
|
||||
(update index-map parent-id (fnil conj []) [id parent-index])))
|
||||
index-map (reduce process-id {} ids)]
|
||||
(-> changes (update-indices index-map))))
|
||||
|
||||
(defn update-indices
|
||||
"Fixes the indices for a set of changes after a duplication. We need to
|
||||
fix the indices to take into the account the movement of indices.
|
||||
|
||||
index-map is a map that goes from parent-id => vector([id index-in-parent])"
|
||||
[changes index-map]
|
||||
(let [inc-indices
|
||||
inc-indices
|
||||
(fn [[offset result] [id index]]
|
||||
[(inc offset) (conj result [id (+ index offset)])])
|
||||
|
||||
|
@ -388,14 +378,12 @@
|
|||
(second)
|
||||
(into {})))
|
||||
|
||||
objects-indices (->> index-map (d/mapm fix-indices) (vals) (reduce merge))
|
||||
objects-indices (->> index-map (d/mapm fix-indices) (vals) (reduce merge))]
|
||||
|
||||
update-change
|
||||
(fn [change]
|
||||
(let [index (get objects-indices (:old-id change))]
|
||||
(-> change
|
||||
(assoc :index index))))]
|
||||
(mapv update-change changes)))
|
||||
(pcb/amend-changes
|
||||
changes
|
||||
(fn [change]
|
||||
(assoc change :index (get objects-indices (:old-id change)))))))
|
||||
|
||||
(defn clear-memorize-duplicated
|
||||
[]
|
||||
|
@ -457,17 +445,14 @@
|
|||
(calc-duplicate-delta obj state objects))
|
||||
(gpt/point 0 0))
|
||||
|
||||
unames (dwc/retrieve-used-names objects)
|
||||
|
||||
rchanges (->> (prepare-duplicate-changes objects page-id unames selected delta)
|
||||
(duplicate-changes-update-indices objects selected))
|
||||
|
||||
uchanges (mapv #(array-map :type :del-obj :page-id page-id :id (:id %))
|
||||
(reverse rchanges))
|
||||
changes (->> (prepare-duplicate-changes objects objects page-id selected delta it)
|
||||
(duplicate-changes-update-indices objects selected))
|
||||
|
||||
id-original (when (= (count selected) 1) (first selected))
|
||||
|
||||
selected (->> rchanges
|
||||
selected (->> changes
|
||||
:redo-changes
|
||||
(filter #(= (:type %) :add-obj))
|
||||
(filter #(selected (:old-id %)))
|
||||
(map #(get-in % [:obj :id]))
|
||||
(into (d/ordered-set)))
|
||||
|
@ -475,9 +460,7 @@
|
|||
id-duplicated (when (= (count selected) 1) (first selected))]
|
||||
|
||||
(rx/of (select-shapes selected)
|
||||
(dch/commit-changes {:redo-changes rchanges
|
||||
:undo-changes uchanges
|
||||
:origin it})
|
||||
(dch/commit-changes changes)
|
||||
(memorize-duplicated id-original id-duplicated)))))))
|
||||
|
||||
(defn change-hover-state
|
||||
|
|
Loading…
Add table
Reference in a new issue