@@ -20,7 +20,8 @@ import {
2020 clear_text_content ,
2121 create_text ,
2222 get_first_child ,
23- get_next_sibling
23+ get_next_sibling ,
24+ should_defer_append
2425} from '../operations.js' ;
2526import {
2627 block ,
@@ -35,10 +36,10 @@ import { source, mutable_source, internal_set } from '../../reactivity/sources.j
3536import { array_from , is_array } from '../../../shared/utils.js' ;
3637import { INERT } from '../../constants.js' ;
3738import { queue_micro_task } from '../task.js' ;
38- import { active_effect , active_reaction , get } from '../../runtime.js' ;
39+ import { active_effect , get } from '../../runtime.js' ;
3940import { DEV } from 'esm-env' ;
4041import { derived_safe_equal } from '../../reactivity/deriveds.js' ;
41- import { find_boundary } from './boundary.js' ;
42+ import { add_boundary_callback , find_boundary } from './boundary.js' ;
4243
4344/**
4445 * The row of a keyed each block that is currently updating. We track this
@@ -64,17 +65,18 @@ export function index(_, i) {
6465 * Pause multiple effects simultaneously, and coordinate their
6566 * subsequent destruction. Used in each blocks
6667 * @param {EachState } state
67- * @param {EachItem[] } items
68+ * @param {EachItem[] } to_destroy
6869 * @param {null | Node } controlled_anchor
69- * @param {Map<any, EachItem> } items_map
7070 */
71- function pause_effects ( state , items , controlled_anchor , items_map ) {
71+ function pause_effects ( state , to_destroy , controlled_anchor ) {
72+ var items_map = state . items ;
73+
7274 /** @type {TransitionManager[] } */
7375 var transitions = [ ] ;
74- var length = items . length ;
76+ var length = to_destroy . length ;
7577
7678 for ( var i = 0 ; i < length ; i ++ ) {
77- pause_children ( items [ i ] . e , transitions , true ) ;
79+ pause_children ( to_destroy [ i ] . e , transitions , true ) ;
7880 }
7981
8082 var is_controlled = length > 0 && transitions . length === 0 && controlled_anchor !== null ;
@@ -87,12 +89,12 @@ function pause_effects(state, items, controlled_anchor, items_map) {
8789 clear_text_content ( parent_node ) ;
8890 parent_node . append ( /** @type {Element } */ ( controlled_anchor ) ) ;
8991 items_map . clear ( ) ;
90- link ( state , items [ 0 ] . prev , items [ length - 1 ] . next ) ;
92+ link ( state , to_destroy [ 0 ] . prev , to_destroy [ length - 1 ] . next ) ;
9193 }
9294
9395 run_out_transitions ( transitions , ( ) => {
9496 for ( var i = 0 ; i < length ; i ++ ) {
95- var item = items [ i ] ;
97+ var item = to_destroy [ i ] ;
9698 if ( ! is_controlled ) {
9799 items_map . delete ( item . k ) ;
98100 link ( state , item . prev , item . next ) ;
@@ -139,6 +141,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
139141
140142 var boundary = find_boundary ( active_effect ) ;
141143
144+ /** @type {Map<any, EachItem> } */
145+ var pending_items = new Map ( ) ;
146+
142147 // TODO: ideally we could use derived for runes mode but because of the ability
143148 // to use a store which can be mutated, we can't do that here as mutating a store
144149 // will still result in the collection array being the same from the store
@@ -151,8 +156,21 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
151156 /** @type {V[] } */
152157 var array ;
153158
159+ /** @type {Effect } */
160+ var each_effect ;
161+
154162 function commit ( ) {
155- reconcile ( array , state , anchor , render_fn , flags , get_key , get_collection ) ;
163+ reconcile (
164+ each_effect ,
165+ array ,
166+ state ,
167+ pending_items ,
168+ anchor ,
169+ render_fn ,
170+ flags ,
171+ get_key ,
172+ get_collection
173+ ) ;
156174
157175 if ( fallback_fn !== null ) {
158176 if ( array . length === 0 ) {
@@ -170,6 +188,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
170188 }
171189
172190 block ( ( ) => {
191+ // store a reference to the effect so that we can update the start/end nodes in reconciliation
192+ each_effect ??= /** @type {Effect } */ ( active_effect ) ;
193+
173194 array = get ( each_array ) ;
174195 var length = array . length ;
175196
@@ -247,7 +268,42 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
247268 fallback = branch ( ( ) => fallback_fn ( anchor ) ) ;
248269 }
249270 } else {
250- commit ( ) ;
271+ var defer = boundary !== null && should_defer_append ( ) ;
272+
273+ if ( defer ) {
274+ for ( i = 0 ; i < length ; i += 1 ) {
275+ value = array [ i ] ;
276+ key = get_key ( value , i ) ;
277+
278+ var existing = state . items . get ( key ) ?? pending_items . get ( key ) ;
279+
280+ if ( existing ) {
281+ // update before reconciliation, to trigger any async updates
282+ if ( ( flags & ( EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE ) ) !== 0 ) {
283+ update_item ( existing , value , i , flags ) ;
284+ }
285+ } else {
286+ var item = create_item (
287+ null ,
288+ state ,
289+ null ,
290+ null ,
291+ value ,
292+ key ,
293+ i ,
294+ render_fn ,
295+ flags ,
296+ get_collection
297+ ) ;
298+
299+ pending_items . set ( key , item ) ;
300+ }
301+ }
302+
303+ add_boundary_callback ( boundary , commit ) ;
304+ } else {
305+ commit ( ) ;
306+ }
251307 }
252308
253309 if ( mismatch ) {
@@ -272,16 +328,28 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
272328/**
273329 * Add, remove, or reorder items output by an each block as its input changes
274330 * @template V
331+ * @param {Effect } each_effect
275332 * @param {Array<V> } array
276333 * @param {EachState } state
334+ * @param {Map<any, EachItem> } pending_items
277335 * @param {Element | Comment | Text } anchor
278336 * @param {(anchor: Node, item: MaybeSource<V>, index: number | Source<number>, collection: () => V[]) => void } render_fn
279337 * @param {number } flags
280338 * @param {(value: V, index: number) => any } get_key
281339 * @param {() => V[] } get_collection
282340 * @returns {void }
283341 */
284- function reconcile ( array , state , anchor , render_fn , flags , get_key , get_collection ) {
342+ function reconcile (
343+ each_effect ,
344+ array ,
345+ state ,
346+ pending_items ,
347+ anchor ,
348+ render_fn ,
349+ flags ,
350+ get_key ,
351+ get_collection
352+ ) {
285353 var is_animated = ( flags & EACH_IS_ANIMATED ) !== 0 ;
286354 var should_update = ( flags & ( EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE ) ) !== 0 ;
287355
@@ -333,7 +401,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key, get_collecti
333401 for ( i = 0 ; i < length ; i += 1 ) {
334402 value = array [ i ] ;
335403 key = get_key ( value , i ) ;
336- item = items . get ( key ) ;
404+ item = items . get ( key ) ?? pending_items . get ( key ) ;
337405
338406 if ( item === undefined ) {
339407 var child_anchor = current ? /** @type {TemplateNode } */ ( current . e . nodes_start ) : anchor ;
@@ -468,7 +536,7 @@ function reconcile(array, state, anchor, render_fn, flags, get_key, get_collecti
468536 }
469537 }
470538
471- pause_effects ( state , to_destroy , controlled_anchor , items ) ;
539+ pause_effects ( state , to_destroy , controlled_anchor ) ;
472540 }
473541 }
474542
@@ -481,8 +549,13 @@ function reconcile(array, state, anchor, render_fn, flags, get_key, get_collecti
481549 } ) ;
482550 }
483551
484- /** @type {Effect } */ ( active_effect ) . first = state . first && state . first . e ;
485- /** @type {Effect } */ ( active_effect ) . last = prev && prev . e ;
552+ // TODO this seems super weird... should be `each_effect`, but that doesn't seem to work?
553+ if ( active_effect !== null ) {
554+ active_effect . first = state . first && state . first . e ;
555+ active_effect . last = prev && prev . e ;
556+ }
557+
558+ pending_items . clear ( ) ;
486559}
487560
488561/**
@@ -506,7 +579,7 @@ function update_item(item, value, index, type) {
506579
507580/**
508581 * @template V
509- * @param {Node } anchor
582+ * @param {Node | null } anchor
510583 * @param {EachState } state
511584 * @param {EachItem | null } prev
512585 * @param {EachItem | null } next
@@ -562,7 +635,12 @@ function create_item(
562635 current_each_item = item ;
563636
564637 try {
565- item . e = branch ( ( ) => render_fn ( anchor , v , i , get_collection ) , hydrating ) ;
638+ if ( anchor === null ) {
639+ var fragment = document . createDocumentFragment ( ) ;
640+ fragment . append ( ( anchor = document . createComment ( '' ) ) ) ;
641+ }
642+
643+ item . e = branch ( ( ) => render_fn ( /** @type {Node } */ ( anchor ) , v , i , get_collection ) , hydrating ) ;
566644
567645 item . e . prev = prev && prev . e ;
568646 item . e . next = next && next . e ;
@@ -596,7 +674,7 @@ function move(item, next, anchor) {
596674 var dest = next ? /** @type {TemplateNode } */ ( next . e . nodes_start ) : anchor ;
597675 var node = /** @type {TemplateNode } */ ( item . e . nodes_start ) ;
598676
599- while ( node !== end ) {
677+ while ( node !== null && node !== end ) {
600678 var next_node = /** @type {TemplateNode } */ ( get_next_sibling ( node ) ) ;
601679 dest . before ( node ) ;
602680 node = next_node ;
0 commit comments