Skip to content

Commit e1cf85a

Browse files
committed
fix: measure elements before taking siblings out of the flow
1 parent 3a6a482 commit e1cf85a

File tree

3 files changed

+64
-5
lines changed

3 files changed

+64
-5
lines changed

packages/svelte/src/internal/client/dom/blocks/each.js

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
} from '../../reactivity/effects.js';
2525
import { source, mutable_source, set } from '../../reactivity/sources.js';
2626
import { is_array, is_frozen } from '../../utils.js';
27-
import { STATE_SYMBOL } from '../../constants.js';
27+
import { INERT, STATE_SYMBOL } from '../../constants.js';
2828

2929
/**
3030
* The row of a keyed each block that is currently updating. We track this
@@ -238,8 +238,8 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
238238
/** @type {import('#client').EachState | import('#client').EachItem} */
239239
var prev = state;
240240

241-
/** @type {import('#client').EachItem[]} */
242-
var to_animate = [];
241+
/** @type {Set<import('#client').EachItem>} */
242+
var to_animate = new Set();
243243

244244
/** @type {import('#client').EachItem[]} */
245245
var matched = [];
@@ -267,7 +267,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
267267

268268
if (item !== undefined) {
269269
item.a?.measure();
270-
to_animate.push(item);
270+
to_animate.add(item);
271271
}
272272
}
273273
}
@@ -302,7 +302,13 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
302302
update_item(item, value, i, flags);
303303
}
304304

305-
resume_effect(item.e);
305+
if ((item.e.f & INERT) !== 0) {
306+
resume_effect(item.e);
307+
if (is_animated) {
308+
item.a?.unfix();
309+
to_animate.delete(item);
310+
}
311+
}
306312

307313
if (item !== current) {
308314
if (seen.has(item)) {
@@ -380,6 +386,16 @@ function reconcile(array, state, anchor, render_fn, flags, get_key) {
380386

381387
var controlled_anchor = (flags & EACH_IS_CONTROLLED) !== 0 && length === 0 ? anchor : null;
382388

389+
if (is_animated) {
390+
for (var i = 0; i < to_destroy.length; i += 1) {
391+
to_destroy[i].a?.measure();
392+
}
393+
394+
for (var i = 0; i < to_destroy.length; i += 1) {
395+
to_destroy[i].a?.fix();
396+
}
397+
}
398+
383399
pause_effects(to_destroy, controlled_anchor, () => {
384400
for (var i = 0; i < to_destroy.length; i += 1) {
385401
var item = to_destroy[i];

packages/svelte/src/internal/client/dom/elements/transitions.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,9 @@ export function animation(element, get_fn, get_params) {
8888
/** @type {import('#client').Animation | undefined} */
8989
var animation;
9090

91+
/** @type {null | { position: string, width: string, height: string }} */
92+
var original_styles = null;
93+
9194
item.a ??= {
9295
element,
9396
measure() {
@@ -111,6 +114,38 @@ export function animation(element, get_fn, get_params) {
111114
animation = undefined;
112115
});
113116
}
117+
},
118+
fix() {
119+
var computed_style = getComputedStyle(element);
120+
121+
if (computed_style.position !== 'absolute' && computed_style.position !== 'fixed') {
122+
var style = /** @type {HTMLElement | SVGElement} */ (element).style;
123+
124+
original_styles = {
125+
position: style.position,
126+
width: style.width,
127+
height: style.height
128+
};
129+
130+
style.position = 'absolute';
131+
style.width = computed_style.width;
132+
style.height = computed_style.height;
133+
var to = element.getBoundingClientRect();
134+
135+
if (from.left !== to.left || from.top !== to.top) {
136+
var transform = `translate(${from.left - to.left}px, ${from.top - to.top}px)`;
137+
style.transform = style.transform ? `${style.transform} ${transform}` : transform;
138+
}
139+
}
140+
},
141+
unfix() {
142+
if (original_styles) {
143+
var style = /** @type {HTMLElement | SVGElement} */ (element).style;
144+
145+
style.position = original_styles.position;
146+
style.width = original_styles.width;
147+
style.height = original_styles.height;
148+
}
114149
}
115150
};
116151

@@ -305,6 +340,10 @@ function animate(element, options, counterpart, t2, callback) {
305340
animation.finished
306341
.then(() => {
307342
callback?.();
343+
344+
if (t2 === 1) {
345+
animation.cancel();
346+
}
308347
})
309348
.catch((e) => {
310349
// Error for DOMException: The user aborted a request. This results in two things:

packages/svelte/src/internal/client/types.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ export interface AnimationManager {
8989
measure: () => void;
9090
/** Called during keyed each block reconciliation, after updates — this triggers the animation */
9191
apply: () => void;
92+
/** Fix the element position, so that siblings can move to the correct destination */
93+
fix: () => void;
94+
/** Unfix the element position if the outro is aborted */
95+
unfix: () => void;
9296
}
9397

9498
export interface Animation {

0 commit comments

Comments
 (0)