🐛 Keep the swap slot when duplicating a swapped nested copy

This commit is contained in:
Pablo Alba 2026-11-11 09:47:48 +01:00 committed by Pablo Alba
parent 54b1eb5d5f
commit 3eebe92606
4 changed files with 104 additions and 2 deletions

View File

@ -3165,6 +3165,7 @@
is-component-main? (ctk/main-instance? obj)
subinstance-head? (ctk/subinstance-head? obj)
instance-root? (ctk/instance-root? obj)
parent-not-in-copy (not (ctk/in-component-copy? parent))
into-component? (and duplicating-component?
(ctn/in-any-component? objects parent))
@ -3174,6 +3175,8 @@
(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
(fn [changes shape]
(let [[_ changes] (generate-add-component-changes changes shape objects file-id (:id page) variant-props)]
@ -3188,7 +3191,8 @@
(cond-> (and (not instance-root?)
subinstance-head?
remove-swap-slot?)
remove-swap-slot?
parent-not-in-copy)
(ctk/remove-swap-slot))
(dissoc :shapes

View File

@ -196,7 +196,7 @@
;; 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))]
common-count (count (take-while (fn [[a b]] (= a b)) pairs))]
(- (count orig-heads) common-count)))

View File

@ -567,3 +567,62 @@
;; blue2' has not swap-id after move
(t/is (some? blue2'))
(t/is (nil? (ctk/get-swap-slot blue2')))))
(defn- setup-nested-swap-file
[]
;; {:frame-icon-a} [:name Frame1] # [Component :icon-a]
;; {:frame-icon-b} [:name Frame1] # [Component :icon-b]
;; {:frame-card} [:name Frame1] # [Component :card]
;; :frame-inner [:name Frame1]
;; :icon-copy [:name Frame1] @--> :frame-icon-a
;; :card-copy [:name Frame1] @--> :frame-card
;; :inner-copy [:name Frame1] @--> :frame-inner
;; :icon-in-card-copy [:name Frame1] @--> :frame-icon-a
;; swapped to :icon-b, it becomes
;; :icon-b-copy [:name Frame1, swap-slot] @--> :frame-icon-b
(-> (thf/sample-file :file1)
(tho/add-frame :frame-icon-a)
(thc/make-component :icon-a :frame-icon-a)
(tho/add-frame :frame-icon-b)
(thc/make-component :icon-b :frame-icon-b)
(tho/add-frame :frame-card)
(thc/make-component :card :frame-card)
(tho/add-frame :frame-inner :parent-label :frame-card)
(thc/instantiate-component :icon-a :icon-copy :parent-label :frame-inner)
(thc/instantiate-component :card :card-copy
:children-labels [:inner-copy :icon-in-card-copy])
(thc/component-swap :icon-in-card-copy :icon-b :icon-b-copy)))
(t/deftest test-keep-swap-slot-duplicating-swapped-nested-copy-inside-copy
(let [;; ==== Setup
file (setup-nested-swap-file)
page (thf/current-page file)
icon-b-copy (ths/get-shape file :icon-b-copy)
;; ==== Action
changes (-> (pcb/empty-changes nil)
(cll/generate-duplicate-changes (:objects page) ;; objects
page ;; page
#{(:id icon-b-copy)} ;; ids
(gpt/point 0 0) ;; delta
{(:id file) file} ;; libraries
(:data file) ;; library-data
(:id file)) ;; file-id
(cll/generate-duplicate-changes-update-indices (:objects page) ;; objects
#{(:id icon-b-copy)})) ;; ids
file' (thf/apply-changes file changes)
;; ==== Get
page' (thf/current-page file')
duplicated-icon-b' (find-duplicated-shape icon-b-copy page')]
;; ==== Check
;; icon-b-copy has swap-slot
(t/is (some? (ctk/get-swap-slot icon-b-copy)))
;; duplicated icon-b copy keeps its swap-slot
(t/is (some? duplicated-icon-b'))
(t/is (some? (ctk/get-swap-slot duplicated-icon-b')))))

View File

@ -291,3 +291,42 @@
(t/testing "two ordinary outside shapes are both kept"
(t/is (= [outside-id outside-2-id]
(vec (ctc/ids-for-structure-op objects [outside-id outside-2-id])))))))
(t/deftest get-nesting-level-delta
(let [page-id uuid/zero
head-id (uuid/next)
frame-id (uuid/next)
sub-id (uuid/next)
head-2-id (uuid/next)
plain-id (uuid/next)
;; copy head with a plain frame and a nested copy head inside
head (make-shape head-id
:parent-id page-id
:type :frame
:component-id (uuid/next)
:component-root true
:shape-ref (uuid/next))
frame (make-shape frame-id :parent-id head-id :type :frame :shape-ref (uuid/next))
sub (make-shape sub-id
:parent-id frame-id
:type :frame
:component-id (uuid/next)
:shape-ref (uuid/next))
head-2 (make-shape head-2-id
:parent-id page-id
:type :frame
:component-id (uuid/next)
:component-root true
:shape-ref (uuid/next))
plain (make-shape plain-id :parent-id page-id)
root (root-shape)
objects (objects-map root head frame sub head-2 plain)]
(t/testing "shape stays under the same copy head"
(t/is (= 0 (ctc/get-nesting-level-delta objects sub frame))))
(t/testing "shape leaves its copy head to the page"
(t/is (= 1 (ctc/get-nesting-level-delta objects sub root))))
(t/testing "shape moves to a different copy head"
(t/is (= 1 (ctc/get-nesting-level-delta objects sub head-2))))
(t/testing "shape outside any copy head goes inside one"
(t/is (= 0 (ctc/get-nesting-level-delta objects plain frame))))))