0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-04-15 16:31:25 -05:00

🐛 Fix position problems cutting-pasting a component

This commit is contained in:
Pablo Alba 2025-04-02 12:10:18 +02:00 committed by GitHub
parent e28f8cae74
commit 1b8714fe7f
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 54 additions and 46 deletions

View file

@ -14,6 +14,7 @@
- Fix path having a wrong selrect [Taiga #10257](https://tree.taiga.io/project/penpot/issue/10257)
- Fix SVG `stroke-linecap` property when importing SVGs [Taiga #9489](https://tree.taiga.io/project/penpot/issue/9489)
- Fix position problems cutting-pasting a component [Taiga #10677](https://tree.taiga.io/project/penpot/issue/10677)
## 2.6.0 (Unreleased)

View file

@ -345,7 +345,9 @@
[:map {:title "DelComponentChange"}
[:type [:= :del-component]]
[:id ::sm/uuid]
[:main-instance {:optional true} :any]
;; when it is an undo of a cut-paste, we need to undo the movement
;; of the shapes so we need to move them delta
[:delta {:optional true} ::gpt/point]
[:skip-undelete? {:optional true} :boolean]]]
[:restore-component
@ -960,8 +962,8 @@
(ctkl/mod-component data params))
(defmethod process-change :del-component
[data {:keys [id skip-undelete? main-instance]}]
(ctf/delete-component data id skip-undelete? main-instance))
[data {:keys [id skip-undelete? delta]}]
(ctf/delete-component data id skip-undelete? delta))
(defmethod process-change :restore-component
[data {:keys [id page-id parent-id]}]

View file

@ -1041,7 +1041,7 @@
:page-id page-id})))
(defn restore-component
[changes id page-id main-instance parent-id]
[changes id page-id delta parent-id]
(assert-library! changes)
(-> changes
(update :redo-changes conj {:type :restore-component
@ -1050,7 +1050,7 @@
:parent-id parent-id})
(update :undo-changes conj {:type :del-component
:id id
:main-instance main-instance})))
:delta delta})))
(defn reorder-grid-children
[changes ids]

View file

@ -359,18 +359,26 @@
(when (some #(= (:id current-page) %) (:pages library-data)) ;; If the page doesn't belong to the library, it's not valid
current-page)
(ctpl/get-last-page library-data))]
(prepare-restore-component changes library-data component-id page (gpt/point 0 0) nil nil nil)))
(prepare-restore-component changes library-data component-id page nil nil nil nil)))
([changes library-data component-id page delta old-id parent-id frame-id]
([changes library-data component-id page position old-id parent-id frame-id]
(let [component (ctkl/get-deleted-component library-data component-id)
parent (get-in page [:objects parent-id])
main-inst (get-in component [:objects (:main-instance-id component)])
inside-component? (some? (ctn/get-instance-root (:objects page) parent))
shapes (cfh/get-children-with-self (:objects component) (:main-instance-id component))
shapes (map #(gsh/move % delta) shapes)
is-variant? (ctk/is-variant? component)
first-shape (cond-> (first shapes)
orig-pos (gpt/point (:x main-inst) (:y main-inst))
delta (if position
(gpt/subtract position orig-pos)
(gpt/point 0 0))
minusdelta (gpt/point (- (:x delta)) (- (:y delta)))
moved-shapes (map #(gsh/move % delta) shapes)
first-shape (cond-> (first moved-shapes)
(not (nil? parent-id))
(assoc :parent-id parent-id)
(not (nil? frame-id))
@ -394,9 +402,9 @@
(some? old-id) (pcb/amend-last-change #(assoc % :old-id old-id))) ; on copy/paste old id is used later to reorder the paster layers
changes (reduce #(pcb/add-object %1 %2 {:ignore-touched true})
changes
(rest shapes))]
{:changes (pcb/restore-component changes component-id (:id page) main-inst parent-id)
:shape (first shapes)})))
(rest moved-shapes))]
{:changes (pcb/restore-component changes component-id (:id page) minusdelta parent-id)
:shape (first moved-shapes)})))
;; ---- General library synchronization functions ----
@ -2188,20 +2196,16 @@
;; When we duplicate a variant along with its variant-container, we will duplicate it
in-variant-container? (contains? ids-map (:variant-id main))
restore-component
#(let [origin-frame (get-in page [:objects frame-id])
delta (cond-> delta
(some? origin-frame)
(gpt/subtract (-> origin-frame :selrect gpt/point)))
{:keys [shape changes]} (prepare-restore-component changes
library-data
component-id
page
delta
main-id
parent-id
frame-id)]
#(let [{:keys [shape changes]}
(prepare-restore-component changes
library-data
component-id
page
pos
main-id
parent-id
frame-id)]
[shape changes])
[_shape changes]

View file

@ -403,33 +403,34 @@
(defn load-component-objects
"Add an :objects property to the component, with only the shapes that belong to it"
[file-data component]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if (and components-v2 component (empty? (:objects component))) ;; This operation may be called twice, e.g. in an idempotent change
(let [component-page (get-component-page file-data component)
page-objects (:objects component-page)
objects (->> (cons (:main-instance-id component)
(cfh/get-children-ids page-objects (:main-instance-id component)))
(map #(get page-objects %))
(d/index-by :id))]
(assoc component :objects objects))
component)))
([file-data component]
(load-component-objects file-data component (gpt/point 0 0)))
([file-data component delta]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
(if (and components-v2 component (empty? (:objects component))) ;; This operation may be called twice, e.g. in an idempotent change
(let [component-page (get-component-page file-data component)
page-objects (:objects component-page)
objects (->> (cons (:main-instance-id component)
(cfh/get-children-ids page-objects (:main-instance-id component)))
(map #(get page-objects %))
;; when it is an undo of a cut-paste, we need to undo the movement
;; of the shapes so we need to move them delta
(map #(gsh/move % delta))
(d/index-by :id))]
(assoc component :objects objects))
component))))
(defn delete-component
"Mark a component as deleted and store the main instance shapes iside it, to
be able to be recovered later."
[file-data component-id skip-undelete? main-instance]
(let [components-v2 (dm/get-in file-data [:options :components-v2])]
[file-data component-id skip-undelete? delta]
(let [components-v2 (dm/get-in file-data [:options :components-v2])
delta (or delta (gpt/point 0 0))]
(if (or (not components-v2) skip-undelete?)
(ctkl/delete-component file-data component-id)
(let [set-main-instance ;; If there is a saved main-instance, restore it. This happens on the restore-component action
#(if main-instance
(assoc-in % [:objects (:main-instance-id %)] main-instance)
%)]
(-> file-data
(ctkl/update-component component-id (partial load-component-objects file-data))
(ctkl/update-component component-id set-main-instance)
(ctkl/mark-component-deleted component-id))))))
(-> file-data
(ctkl/update-component component-id #(load-component-objects file-data % delta))
(ctkl/mark-component-deleted component-id)))))
(defn restore-component
"Recover a deleted component and all its shapes and put all this again in place."