fix: ensure correct each block element is moved during reconcilation

pull/12182/head
Dominic Gannaway 4 months ago
parent 23484d6875
commit 7d62528f7a

@ -0,0 +1,5 @@
---
"svelte": patch
---
fix: ensure correct each block element is moved during reconcilation

@ -482,10 +482,11 @@ function create_item(anchor, prev, next, value, key, index, render_fn, flags) {
/**
* @param {import('#client').TemplateNode} dom
* @param {import('#client').TemplateNode | null} sibling
* @param {import("#client").Effect} effect
* @returns {import('#client').TemplateNode}
*/
function get_adjusted_first_node(dom, effect) {
function get_adjusted_first_node(dom, sibling, effect) {
if ((dom.nodeType === 3 && /** @type {Text} */ (dom).data === '') || dom.nodeType === 8) {
var adjusted = effect.first;
var next;
@ -498,7 +499,11 @@ function get_adjusted_first_node(dom, effect) {
}
adjusted = next;
}
return get_first_node(/** @type {import("#client").Effect} */ (adjusted));
var adjusted_dom = get_first_node(/** @type {import("#client").Effect} */ (adjusted));
// If we have a sibling that contains the adjusted_dom, then use that instead.
if (sibling?.contains(adjusted_dom)) {
return sibling;
}
}
return dom;
}
@ -511,9 +516,13 @@ function get_adjusted_first_node(dom, effect) {
function get_first_node(effect) {
var dom = effect.dom;
if (is_array(dom)) {
return get_adjusted_first_node(dom[0], effect);
return get_adjusted_first_node(dom[0], dom[1], effect);
}
return get_adjusted_first_node(/** @type {import('#client').TemplateNode} **/ (dom), effect);
return get_adjusted_first_node(
/** @type {import('#client').TemplateNode} **/ (dom),
null,
effect
);
}
/**

@ -0,0 +1,27 @@
import { flushSync } from 'svelte';
import { test } from '../../test';
export default test({
html: `<ul><li>test (1) <span style="background-color: red; width: 20px; height: 20px; display: inline-block;"></span></li><li>test 2 (2)</li><li>test 3 (3)</li></ul><button>Swap items 1 &amp; 3</button>`,
async test({ assert, target }) {
const [btn1] = target.querySelectorAll('button');
flushSync(() => {
btn1.click();
});
flushSync(() => {
btn1.click();
});
flushSync(() => {
btn1.click();
});
assert.htmlEqual(
target.innerHTML,
`<ul><li>test (1) <span style="background-color: red; width: 20px; height: 20px; display: inline-block;"></span></li><li>test 2 (2)</li><li>test 3 (3)</li></ul><button>Swap items 1 &amp; 3</button>`
);
}
});

@ -0,0 +1,26 @@
<script>
const items = $state([
{ name: 'test', id: 1, color: 'red' },
{ name: 'test 2', id: 2 },
{ name: 'test 3', id: 3 },
]);
const onclick = () => {
const from = 0;
const to = 2;
items.splice(to, 0, items.splice(from, 1)[0]);
};
</script>
{#snippet renderItem(item)}
<li>
{item.name} ({item.id})
{#if item.color}<span style="background-color: {item.color}; width: 20px; height: 20px; display: inline-block;"></span>{/if}
</li>
{/snippet}
<ul>
{#each items as item (item.id)}
{@render renderItem(item)}
{/each}
</ul>
<button {onclick}>Swap items 1 & 3</button>
Loading…
Cancel
Save