fix: avoid duplication of content within each blocks when transitioning

fix-each-block-duplications
Dominic Gannaway 5 months ago
parent 02441c6a19
commit c0565191f0

@ -44,33 +44,45 @@ export function set_current_each_item(item) {
/**
* Pause multiple effects simultaneously, and coordinate their
* subsequent destruction. Used in each blocks
* @param {import('#client').Effect[]} effects
* @param {import('#client').EachItem[]} items
* @param {Map<unknown, import('#client').EachItem>} paused
* @param {null | Node} controlled_anchor
* @param {() => void} [callback]
*/
function pause_effects(effects, controlled_anchor, callback) {
function pause_effects(items, paused, controlled_anchor) {
/** @type {import('#client').TransitionManager[]} */
var transitions = [];
var length = effects.length;
var length = items.length;
var transitions_count;
for (var i = 0; i < length; i++) {
pause_children(effects[i], transitions, true);
var item = items[i];
transitions_count = transitions.length;
pause_children(item.e, transitions, true);
if (transitions_count !== transitions.length) {
paused.set(item.k, item);
}
}
// If we have a controlled anchor, it means that the each block is inside a single
// DOM element, so we can apply a fast-path for clearing the contents of the element.
if (effects.length > 0 && transitions.length === 0 && controlled_anchor !== null) {
var parent_node = /** @type {Element} */ (controlled_anchor.parentNode);
parent_node.textContent = '';
parent_node.append(controlled_anchor);
if (items.length > 0 && transitions.length === 0) {
if (controlled_anchor !== null) {
var parent_node = /** @type {Element} */ (controlled_anchor.parentNode);
parent_node.textContent = '';
parent_node.append(controlled_anchor);
}
// We can avoid deleting paused items from the paused map as there are no transitions active.
for (i = 0; i < length; i++) {
destroy_effect(items[i].e);
}
return;
}
run_out_transitions(transitions, () => {
for (var i = 0; i < length; i++) {
destroy_effect(effects[i]);
paused.delete(item.k);
destroy_effect(items[i].e);
}
if (callback !== undefined) callback();
});
}
@ -87,7 +99,7 @@ function pause_effects(effects, controlled_anchor, callback) {
*/
function each(anchor, flags, get_collection, get_key, render_fn, fallback_fn, reconcile_fn) {
/** @type {import('#client').EachState} */
var state = { flags, items: [] };
var state = { flags, items: [], paused: new Map() };
var is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
@ -242,6 +254,7 @@ export function each_indexed(anchor, flags, get_collection, render_fn, fallback_
*/
function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
var a_items = state.items;
var paused = state.paused;
var a = a_items.length;
var b = array.length;
@ -273,16 +286,16 @@ function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
state.items = b_items;
} else if (a > b) {
// remove items
var effects = [];
var items = [];
for (i = b; i < a; i += 1) {
effects.push(a_items[i].e);
items.push(a_items[i]);
}
var controlled_anchor = (flags & EACH_IS_CONTROLLED) !== 0 && b === 0 ? anchor : null;
pause_effects(effects, controlled_anchor, () => {
state.items.length = b;
});
state.items.length = b;
pause_effects(items, paused, controlled_anchor);
}
}
@ -301,6 +314,7 @@ function reconcile_indexed_array(array, state, anchor, render_fn, flags) {
*/
function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
var a_items = state.items;
var paused = state.paused;
var a = a_items.length;
var b = array.length;
@ -314,7 +328,7 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
var start = 0;
var item;
/** @type {import('#client').Effect[]} */
/** @type {import('#client').EachItem[]} */
var to_destroy = [];
/** @type {Array<import('#client').EachItem>} */
var to_animate = [];
@ -356,13 +370,21 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
if (start === a) {
// add only
while (start < b) {
item = create_item(anchor, array[start], keys[start], start, render_fn, flags);
item = paused.get(keys[start]);
if (item === undefined || item.e.dom === null) {
item = create_item(anchor, array[start], keys[start], start, render_fn, flags);
} else {
resume_effect(item.e);
// move(/** @type {import('#client').Dom} */ (item.e.dom), anchor);
}
b_items[start++] = item;
}
} else if (start === b) {
// remove only
while (start < a) {
to_destroy.push(a_items[start++].e);
to_destroy.push(a_items[start++]);
}
} else {
// reconcile
@ -401,7 +423,7 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
resume_effect(item.e);
if (index === undefined) {
to_destroy.push(item.e);
to_destroy.push(item);
} else {
moved = true;
sources[index - start] = i;
@ -421,7 +443,7 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
} else if (is_controlled && to_destroy.length === a_items.length) {
// We can optimize the case in which all items are replaced —
// destroy everything first, then append new items
pause_effects(to_destroy, anchor);
pause_effects(to_destroy, paused, anchor);
to_destroy = [];
}
@ -432,7 +454,14 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
if (should_insert) {
if (last_item !== undefined) anchor = get_first_child(last_item);
item = create_item(anchor, array[b], keys[b], b, render_fn, flags);
item = paused.get(keys[b]);
if (item === undefined || item.e.dom === null) {
item = create_item(anchor, array[b], keys[b], b, render_fn, flags);
} else {
resume_effect(item.e);
// move(/** @type {import('#client').Dom} */ (item.e.dom), anchor);
}
} else {
item = b_items[b];
if (should_update) {
@ -465,9 +494,9 @@ function reconcile_tracked_array(array, state, anchor, render_fn, flags, keys) {
var controlled_anchor = is_controlled && b_items.length === 0 ? anchor : null;
pause_effects(to_destroy, controlled_anchor, () => {
state.items = b_items;
});
state.items = b_items;
pause_effects(to_destroy, paused, controlled_anchor);
}
/**

@ -52,6 +52,8 @@ export type EachState = {
flags: number;
/** items */
items: EachItem[];
/** paused items */
paused: Map<unknown, EachItem>
};
export type EachItem = {

Loading…
Cancel
Save