0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-10 23:01:39 -05:00

🐛 Advance nested copies when duplicated

This commit is contained in:
Andrés Moya 2024-03-19 17:31:24 +01:00 committed by Alejandro Alonso
parent 1d25115218
commit 9a7a99e67a
4 changed files with 75 additions and 15 deletions
common/src/app/common/types
frontend/src/app/main/data

View file

@ -154,6 +154,17 @@
:else :else
(get-head-shape objects (get objects (:parent-id shape)) options)))) (get-head-shape objects (get objects (:parent-id shape)) options))))
(defn get-child-heads
"Get all recursive childs that are heads (when a head is found, do not
continue down looking for subsequent nested heads)."
[objects shape-id]
(let [shape (get objects shape-id)]
(if (nil? shape)
[]
(if (ctk/instance-head? shape)
[shape]
(mapcat #(get-child-heads objects %) (:shapes shape))))))
(defn get-parent-heads (defn get-parent-heads
"Get all component heads that are ancestors of the shape, in top-down order "Get all component heads that are ancestors of the shape, in top-down order
(include self if it's also a head)." (include self if it's also a head)."
@ -170,6 +181,20 @@
(filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %))) (filter #(and (ctk/instance-head? %) (ctk/in-component-copy? %)))
(reverse))) (reverse)))
(defn get-nesting-level-delta
"Get how many levels a shape will 'go up' if moved under the new parent."
[objects shape new-parent]
(let [orig-heads (->> (get-parent-copy-heads objects shape)
(remove #(= (:id %) (:id shape))))
dest-heads (get-parent-copy-heads objects new-parent)
;; Calculate how many parent heads share in common the original
;; shape and the new parent.
pairs (map vector orig-heads dest-heads)
common-count (count (take-while (fn [a b] (= a b)) pairs))]
(- (count orig-heads) common-count)))
(defn get-instance-root (defn get-instance-root
"Get the parent shape at the top of the component instance (main or copy)." "Get the parent shape at the top of the component instance (main or copy)."
[objects shape] [objects shape]

View file

@ -216,14 +216,14 @@
(some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape)))) (some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape))))
(defn find-original-ref-shape (defn advance-shape-ref
"Recursively call to find-ref-shape until find the original shape of the original component" "Get the shape-ref of the near main of the shape, recursively repeated as many times
[file container libraries shape & options] as the given levels."
[file container libraries shape levels & options]
(let [ref-shape (find-ref-shape file container libraries shape options)] (let [ref-shape (find-ref-shape file container libraries shape options)]
(if (nil? (:shape-ref ref-shape)) (if (or (nil? (:shape-ref ref-shape)) (not (pos? levels)))
ref-shape (:id ref-shape)
(find-original-ref-shape file container libraries ref-shape options)))) (advance-shape-ref file container libraries ref-shape (dec levels) options))))
(defn find-ref-component (defn find-ref-component
"Locate the nearest component in the local file or libraries that is referenced by the "Locate the nearest component in the local file or libraries that is referenced by the

View file

@ -25,6 +25,7 @@
[app.common.types.component :as ctk] [app.common.types.component :as ctk]
[app.common.types.components-list :as ctkl] [app.common.types.components-list :as ctkl]
[app.common.types.container :as ctn] [app.common.types.container :as ctn]
[app.common.types.file :as ctf]
[app.common.types.shape :as cts] [app.common.types.shape :as cts]
[app.common.types.shape-tree :as ctst] [app.common.types.shape-tree :as ctst]
[app.common.types.shape.layout :as ctl] [app.common.types.shape.layout :as ctl]
@ -1598,6 +1599,34 @@
(let [frame (get objects parent-frame-id)] (let [frame (get objects parent-frame-id)]
(gsh/translate-to-frame shape frame)))) (gsh/translate-to-frame shape frame))))
;; When copying an instance that is nested inside another one, we need to
;; advance the shape refs to one or more levels of remote mains.
(advance-copies [state selected data]
(let [file (wsh/get-local-file-full state)
libraries (wsh/get-libraries state)
page (wsh/lookup-page state)
heads (mapcat #(ctn/get-child-heads (:objects data) %) selected)]
(update data :objects
#(reduce (partial advance-copy file libraries page)
%
heads))))
(advance-copy [file libraries page objects shape]
(if (and (ctk/instance-head? shape) (not (ctk/main-instance? shape)))
(let [level-delta (ctn/get-nesting-level-delta (:objects page) shape uuid/zero)]
(if (pos? level-delta)
(reduce (partial advance-shape file libraries page level-delta)
objects
(cfh/get-children-with-self objects (:id shape)))
objects))
objects))
(advance-shape [file libraries page level-delta objects shape]
(let [new-shape-ref (ctf/advance-shape-ref file page libraries shape level-delta {:include-deleted? true})]
(cond-> objects
(and (some? new-shape-ref) (not= new-shape-ref (:shape-ref shape)))
(assoc-in [(:id shape) :shape-ref] new-shape-ref))))
(on-copy-error [error] (on-copy-error [error]
(js/console.error "clipboard blocked:" error) (js/console.error "clipboard blocked:" error)
(rx/empty))] (rx/empty))]
@ -1636,6 +1665,7 @@
(rx/merge-map (partial prepare-object objects frame-id)) (rx/merge-map (partial prepare-object objects frame-id))
(rx/reduce collect-data initial) (rx/reduce collect-data initial)
(rx/map (partial sort-selected state)) (rx/map (partial sort-selected state))
(rx/map (partial advance-copies state selected))
(rx/map #(t/encode-str % {:type :json-verbose})) (rx/map #(t/encode-str % {:type :json-verbose}))
(rx/map wapi/write-to-clipboard) (rx/map wapi/write-to-clipboard)
(rx/catch on-copy-error) (rx/catch on-copy-error)

View file

@ -404,6 +404,7 @@
ids-map ids-map
%2 %2
delta delta
nil
libraries libraries
library-data library-data
it it
@ -459,10 +460,10 @@
;; TODO: move to common.files.shape-helpers ;; TODO: move to common.files.shape-helpers
(defn- prepare-duplicate-shape-change (defn- prepare-duplicate-shape-change
([changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id] ([changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id]
(prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id (:frame-id obj) (:parent-id obj) false false)) (prepare-duplicate-shape-change changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id (:frame-id obj) (:parent-id obj) false false))
([changes objects page unames update-unames! ids-map obj delta libraries library-data it file-id frame-id parent-id duplicating-component? child?] ([changes objects page unames update-unames! ids-map obj delta level-delta libraries library-data it file-id frame-id parent-id duplicating-component? child?]
(cond (cond
(nil? obj) (nil? obj)
changes changes
@ -486,11 +487,14 @@
duplicating-component? (or duplicating-component? (ctk/instance-head? obj)) duplicating-component? (or duplicating-component? (ctk/instance-head? obj))
is-component-main? (ctk/main-instance? obj) is-component-main? (ctk/main-instance? obj)
original-ref-shape (-> (ctf/find-original-ref-shape nil page libraries obj {:include-deleted? true})
:id)
into-component? (and duplicating-component? into-component? (and duplicating-component?
(ctn/in-any-component? objects parent)) (ctn/in-any-component? objects parent))
level-delta (if (some? level-delta)
level-delta
(ctn/get-nesting-level-delta objects obj parent))
new-shape-ref (ctf/advance-shape-ref nil page libraries obj level-delta {:include-deleted? true})
regenerate-component regenerate-component
(fn [changes shape] (fn [changes shape]
(let [components-v2 (dm/get-in library-data [:options :components-v2]) (let [components-v2 (dm/get-in library-data [:options :components-v2])
@ -518,9 +522,9 @@
(cond-> (or frame? group? bool?) (cond-> (or frame? group? bool?)
(assoc :shapes [])) (assoc :shapes []))
(cond-> (and (some? original-ref-shape) (cond-> (and (some? new-shape-ref)
(not= original-ref-shape (:shape-ref obj))) (not= new-shape-ref (:shape-ref obj)))
(assoc :shape-ref original-ref-shape)) (assoc :shape-ref new-shape-ref))
(gsh/move delta) (gsh/move delta)
(d/update-when :interactions #(ctsi/remap-interactions % ids-map objects)) (d/update-when :interactions #(ctsi/remap-interactions % ids-map objects))
@ -561,6 +565,7 @@
ids-map ids-map
child child
delta delta
level-delta
libraries libraries
library-data library-data
it it