mirror of
https://github.com/penpot/penpot.git
synced 2026-10-29 14:00:37 +00:00
✨ Place a swapped child where the new variant has it
A variant switch with keep-touched carries a swapped child into the new variant, but left it where it was in the old one. The next sync then moved it to the place of the child it replaces, at a moment unrelated to the switch. The switch now gives it that placement at once, through the same step sync uses: position, constraints and layout item attributes the user did not change. Benchmark, against the same code without this change under the same load (lower quantile, ms; without / with): deepest-edit-through-three-levels 36.0 / 36.0 edit-through-a-chain-of-dependents 65.2 / 70.2 library-update-into-copies 74.3 / 75.9 main-edit-into-10-copies 1.6 / 1.5 main-edit-into-100-copies 16.3 / 15.7 main-edit-into-1000-copies 162.6 / 165.8 variant-switch-into-copies 27.2 / 27.8 AI-assisted-by: claude-opus-5-5 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
7105f3038e
commit
40e2b2934a
@ -2416,19 +2416,20 @@
|
||||
(assoc attr (get origin attr)))))
|
||||
dest))))
|
||||
|
||||
(defn- update-swapped-placement
|
||||
"Synchronizes a swapped copy head with the main shape it replaces, in the
|
||||
attributes that place it in its parent: position, constraints and layout
|
||||
item attributes. Its content comes from its own component. A position set
|
||||
by a layout is left to the layout."
|
||||
[changes shape-inst shape-main root-inst root-main container]
|
||||
(let [objects (:objects container)
|
||||
touched (get shape-inst :touched #{})
|
||||
(defn generate-take-placement
|
||||
"Give the swapped copy head `shape-inst` the placement of `placed`, the
|
||||
shape whose position it takes, expressed in the same coordinates: the
|
||||
attributes that place it in its parent (position, constraints and layout
|
||||
item attributes) that the user did not change. Its content comes from its
|
||||
own component. With `layout-placed?`, the position is left to the layout.
|
||||
`objects` holds the subtree of `shape-inst`, which moves with it."
|
||||
[changes shape-inst placed objects layout-placed?]
|
||||
(let [touched (get shape-inst :touched #{})
|
||||
untouched? (fn [attr]
|
||||
(not (touched (ctk/resolve-sync-group (:type shape-inst) attr))))
|
||||
|
||||
placement (reduce (fn [attrs attr]
|
||||
(let [value (get shape-main attr)]
|
||||
(let [value (get placed attr)]
|
||||
(if (and (untouched? attr)
|
||||
(not= value (get shape-inst attr)))
|
||||
(assoc attrs attr value)
|
||||
@ -2439,12 +2440,10 @@
|
||||
position (fn [shape]
|
||||
(gpt/point (dm/get-in shape [:selrect :x])
|
||||
(dm/get-in shape [:selrect :y])))
|
||||
delta (gpt/subtract (position (reposition-shape shape-main root-main root-inst))
|
||||
(position shape-inst))
|
||||
delta (gpt/subtract (position placed) (position shape-inst))
|
||||
move? (and (untouched? :x)
|
||||
(not (gpt/almost-zero? delta))
|
||||
(or (not (ctsl/any-layout-immediate-child? objects shape-inst))
|
||||
(ctsl/position-absolute? shape-inst)))]
|
||||
(not layout-placed?)
|
||||
(not (gpt/almost-zero? delta)))]
|
||||
(cond-> changes
|
||||
(seq placement)
|
||||
(pcb/update-shapes [(:id shape-inst)]
|
||||
@ -2462,6 +2461,18 @@
|
||||
#(gsh/move % delta)
|
||||
{:ignore-touched true}))))
|
||||
|
||||
(defn- update-swapped-placement
|
||||
"Synchronizes a swapped copy head with the main shape it replaces, in the
|
||||
attributes that place it in its parent."
|
||||
[changes shape-inst shape-main root-inst root-main container]
|
||||
(let [objects (:objects container)]
|
||||
(generate-take-placement changes
|
||||
shape-inst
|
||||
(reposition-shape shape-main root-main root-inst)
|
||||
objects
|
||||
(and (ctsl/any-layout-immediate-child? objects shape-inst)
|
||||
(not (ctsl/position-absolute? shape-inst))))))
|
||||
|
||||
(defn- update-flex-child-copy-attrs
|
||||
"Synchronizes the attributes inside the flex-child items (main->copy)"
|
||||
[changes shape-main shape-copy main-container main-component copy-container omit-touched?]
|
||||
|
||||
@ -102,6 +102,13 @@
|
||||
(= (:component-file %) (:component-file related-shape-in-new)))
|
||||
(ctk/remove-swap-group)))
|
||||
|
||||
;; and its placement, as sync would give it
|
||||
(cll/generate-take-placement orig-swapped-child
|
||||
related-shape-in-new
|
||||
objects
|
||||
(and (ctsl/any-layout-immediate-child? objects related-shape-in-new)
|
||||
(not (ctsl/position-absolute? orig-swapped-child))))
|
||||
|
||||
;; Delete new non-swapped item
|
||||
(cls/generate-delete-shapes ldata page objects (d/ordered-set (:id related-shape-in-new)) {:allow-altering-copies true})
|
||||
second)))
|
||||
|
||||
@ -475,3 +475,14 @@
|
||||
(propagate :card)
|
||||
(thf/validate-file!))]
|
||||
(t/is (= margin (:layout-item-margin (ths/get-shape file :copy-star))))))
|
||||
|
||||
(t/deftest switch-places-the-swap-where-the-new-variant-has-its-child
|
||||
(let [file (-> (setup-variants)
|
||||
(move-shape :child2 30 5))
|
||||
rel (fn [file child parent]
|
||||
(let [[x y] (position file child)
|
||||
[px py] (position file parent)]
|
||||
[(- x px) (- y py)]))
|
||||
file' (tho/swap-component-in-shape file :copy01 :c02 {:keep-touched? true})]
|
||||
(t/is (ctk/swapped? (ths/get-shape file' :copy-child)))
|
||||
(t/is (= (rel file' :child2 :m02) (rel file' :copy-child :copy01)))))
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user