mirror of https://github.com/sveltejs/svelte
parent
358c93853f
commit
c063a71729
@ -0,0 +1,646 @@
|
|||||||
|
import {
|
||||||
|
EACH_INDEX_REACTIVE,
|
||||||
|
EACH_IS_ANIMATED,
|
||||||
|
EACH_IS_CONTROLLED,
|
||||||
|
EACH_IS_PROXIED,
|
||||||
|
EACH_ITEM_REACTIVE
|
||||||
|
} from '../../constants.js';
|
||||||
|
import { create_each_block } from './block.js';
|
||||||
|
import {
|
||||||
|
current_hydration_fragment,
|
||||||
|
get_hydration_fragment,
|
||||||
|
hydrate_block_anchor,
|
||||||
|
set_current_hydration_fragment
|
||||||
|
} from './hydration.js';
|
||||||
|
import { clear_text_content, map_get, map_set } from './operations.js';
|
||||||
|
import { STATE_SYMBOL } from './proxy.js';
|
||||||
|
import { insert, remove } from './reconciler.js';
|
||||||
|
import {
|
||||||
|
destroy_each_item_block,
|
||||||
|
each_item_block,
|
||||||
|
empty,
|
||||||
|
update_each_item_block
|
||||||
|
} from './render.js';
|
||||||
|
import { destroy_signal, execute_effect, push_destroy_fn, render_effect } from './runtime.js';
|
||||||
|
import { trigger_transitions } from './transitions.js';
|
||||||
|
import { is_array } from './utils.js';
|
||||||
|
|
||||||
|
const NEW_BLOCK = -1;
|
||||||
|
const MOVED_BLOCK = 99999999;
|
||||||
|
const LIS_BLOCK = -2;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @param {Element | Comment} anchor_node
|
||||||
|
* @param {() => V[]} collection
|
||||||
|
* @param {number} flags
|
||||||
|
* @param {null | ((item: V) => string)} key_fn
|
||||||
|
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||||
|
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||||
|
* @param {typeof reconcile_indexed_array | reconcile_tracked_array} reconcile_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) {
|
||||||
|
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||||
|
const block = create_each_block(flags, anchor_node);
|
||||||
|
|
||||||
|
/** @type {null | import('./types.js').Render} */
|
||||||
|
let current_fallback = null;
|
||||||
|
hydrate_block_anchor(anchor_node, is_controlled);
|
||||||
|
|
||||||
|
/** @type {V[]} */
|
||||||
|
let array;
|
||||||
|
|
||||||
|
/** @type {Array<string> | null} */
|
||||||
|
let keys = null;
|
||||||
|
|
||||||
|
/** @type {null | import('./types.js').EffectSignal} */
|
||||||
|
let render = null;
|
||||||
|
block.r =
|
||||||
|
/** @param {import('./types.js').Transition} transition */
|
||||||
|
(transition) => {
|
||||||
|
const fallback = /** @type {import('./types.js').Render} */ (current_fallback);
|
||||||
|
const transitions = fallback.s;
|
||||||
|
transitions.add(transition);
|
||||||
|
transition.f(() => {
|
||||||
|
transitions.delete(transition);
|
||||||
|
if (transitions.size === 0) {
|
||||||
|
if (fallback.e !== null) {
|
||||||
|
if (fallback.d !== null) {
|
||||||
|
remove(fallback.d);
|
||||||
|
fallback.d = null;
|
||||||
|
}
|
||||||
|
destroy_signal(fallback.e);
|
||||||
|
fallback.e = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const create_fallback_effect = () => {
|
||||||
|
/** @type {import('./types.js').Render} */
|
||||||
|
const fallback = {
|
||||||
|
d: null,
|
||||||
|
e: null,
|
||||||
|
s: new Set(),
|
||||||
|
p: current_fallback
|
||||||
|
};
|
||||||
|
// Managed effect
|
||||||
|
const effect = render_effect(
|
||||||
|
() => {
|
||||||
|
const dom = block.d;
|
||||||
|
if (dom !== null) {
|
||||||
|
remove(dom);
|
||||||
|
block.d = null;
|
||||||
|
}
|
||||||
|
let anchor = block.a;
|
||||||
|
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
|
||||||
|
if (is_controlled) {
|
||||||
|
anchor = empty();
|
||||||
|
block.a.appendChild(anchor);
|
||||||
|
}
|
||||||
|
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
|
||||||
|
fallback.d = block.d;
|
||||||
|
block.d = null;
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
fallback.e = effect;
|
||||||
|
current_fallback = fallback;
|
||||||
|
};
|
||||||
|
const each = render_effect(
|
||||||
|
() => {
|
||||||
|
/** @type {V[]} */
|
||||||
|
const maybe_array = collection();
|
||||||
|
array = is_array(maybe_array)
|
||||||
|
? maybe_array
|
||||||
|
: maybe_array == null
|
||||||
|
? []
|
||||||
|
: Array.from(maybe_array);
|
||||||
|
if (key_fn !== null) {
|
||||||
|
keys = array.map(key_fn);
|
||||||
|
}
|
||||||
|
const length = array.length;
|
||||||
|
if (fallback_fn !== null) {
|
||||||
|
if (length === 0) {
|
||||||
|
if (block.v.length !== 0 || render === null) {
|
||||||
|
create_fallback_effect();
|
||||||
|
}
|
||||||
|
} else if (block.v.length === 0 && current_fallback !== null) {
|
||||||
|
const fallback = current_fallback;
|
||||||
|
const transitions = fallback.s;
|
||||||
|
if (transitions.size === 0) {
|
||||||
|
if (fallback.d !== null) {
|
||||||
|
remove(fallback.d);
|
||||||
|
fallback.d = null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
trigger_transitions(transitions, 'out');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (render !== null) {
|
||||||
|
execute_effect(render);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
render = render_effect(
|
||||||
|
/** @param {import('./types.js').EachBlock} block */
|
||||||
|
(block) => {
|
||||||
|
const flags = block.f;
|
||||||
|
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||||
|
const anchor_node = block.a;
|
||||||
|
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, true, keys);
|
||||||
|
},
|
||||||
|
block,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
push_destroy_fn(each, () => {
|
||||||
|
const flags = block.f;
|
||||||
|
const anchor_node = block.a;
|
||||||
|
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
|
||||||
|
let fallback = current_fallback;
|
||||||
|
while (fallback !== null) {
|
||||||
|
const dom = fallback.d;
|
||||||
|
if (dom !== null) {
|
||||||
|
remove(dom);
|
||||||
|
}
|
||||||
|
const effect = fallback.e;
|
||||||
|
if (effect !== null) {
|
||||||
|
destroy_signal(effect);
|
||||||
|
}
|
||||||
|
fallback = fallback.p;
|
||||||
|
}
|
||||||
|
// Clear the array
|
||||||
|
reconcile_fn([], block, anchor_node, is_controlled, render_fn, flags, false, keys);
|
||||||
|
destroy_signal(/** @type {import('./types.js').EffectSignal} */ (render));
|
||||||
|
});
|
||||||
|
block.e = each;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @param {Element | Comment} anchor_node
|
||||||
|
* @param {() => V[]} collection
|
||||||
|
* @param {number} flags
|
||||||
|
* @param {null | ((item: V) => string)} key_fn
|
||||||
|
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||||
|
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function each_keyed(anchor_node, collection, flags, key_fn, render_fn, fallback_fn) {
|
||||||
|
each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_tracked_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @param {Element | Comment} anchor_node
|
||||||
|
* @param {() => V[]} collection
|
||||||
|
* @param {number} flags
|
||||||
|
* @param {(anchor: null, item: V, index: import('./types.js').MaybeSignal<number>) => void} render_fn
|
||||||
|
* @param {null | ((anchor: Node) => void)} fallback_fn
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function each_indexed(anchor_node, collection, flags, render_fn, fallback_fn) {
|
||||||
|
each(anchor_node, collection, flags, null, render_fn, fallback_fn, reconcile_indexed_array);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @param {Array<V>} array
|
||||||
|
* @param {import('./types.js').EachBlock} each_block
|
||||||
|
* @param {Element | Comment | Text} dom
|
||||||
|
* @param {boolean} is_controlled
|
||||||
|
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
|
||||||
|
* @param {number} flags
|
||||||
|
* @param {boolean} apply_transitions
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function reconcile_indexed_array(
|
||||||
|
array,
|
||||||
|
each_block,
|
||||||
|
dom,
|
||||||
|
is_controlled,
|
||||||
|
render_fn,
|
||||||
|
flags,
|
||||||
|
apply_transitions
|
||||||
|
) {
|
||||||
|
var is_proxied_array = STATE_SYMBOL in array;
|
||||||
|
var a_blocks = each_block.v;
|
||||||
|
var active_transitions = each_block.s;
|
||||||
|
|
||||||
|
if (is_proxied_array) {
|
||||||
|
flags &= ~EACH_ITEM_REACTIVE;
|
||||||
|
flags |= EACH_IS_PROXIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {number | void} */
|
||||||
|
var a = a_blocks.length;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var b = array.length;
|
||||||
|
var length = Math.max(a, b);
|
||||||
|
var index = 0;
|
||||||
|
|
||||||
|
/** @type {Array<import('./types.js').EachItemBlock>} */
|
||||||
|
var b_blocks;
|
||||||
|
var block;
|
||||||
|
if (active_transitions.length !== 0) {
|
||||||
|
destroy_active_transition_blocks(active_transitions);
|
||||||
|
}
|
||||||
|
if (b === 0) {
|
||||||
|
b_blocks = [];
|
||||||
|
// Remove old blocks
|
||||||
|
if (is_controlled && a !== 0) {
|
||||||
|
clear_text_content(dom);
|
||||||
|
}
|
||||||
|
while (index < length) {
|
||||||
|
block = a_blocks[index++];
|
||||||
|
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var item;
|
||||||
|
b_blocks = Array(b);
|
||||||
|
if (current_hydration_fragment !== null) {
|
||||||
|
/** @type {Node} */
|
||||||
|
var hydrating_node = current_hydration_fragment[0];
|
||||||
|
for (; index < length; index++) {
|
||||||
|
// Hydrate block
|
||||||
|
item = array[index];
|
||||||
|
var fragment = /** @type {Array<Text | Comment | Element>} */ (
|
||||||
|
get_hydration_fragment(hydrating_node)
|
||||||
|
);
|
||||||
|
set_current_hydration_fragment(fragment);
|
||||||
|
hydrating_node = /** @type {Node} */ (
|
||||||
|
/** @type {Node} */ (/** @type {Node} */ (fragment.at(-1)).nextSibling).nextSibling
|
||||||
|
);
|
||||||
|
block = each_item_block(array, item, null, index, render_fn, flags);
|
||||||
|
b_blocks[index] = block;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (; index < length; index++) {
|
||||||
|
if (index >= a) {
|
||||||
|
// Add block
|
||||||
|
item = array[index];
|
||||||
|
block = each_item_block(array, item, null, index, render_fn, flags);
|
||||||
|
b_blocks[index] = block;
|
||||||
|
insert_each_item_block(block, dom, is_controlled, null);
|
||||||
|
} else if (index >= b) {
|
||||||
|
// Remove block
|
||||||
|
block = a_blocks[index];
|
||||||
|
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||||
|
} else {
|
||||||
|
// Update block
|
||||||
|
item = array[index];
|
||||||
|
block = a_blocks[index];
|
||||||
|
b_blocks[index] = block;
|
||||||
|
update_each_item_block(block, item, index, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
each_block.v = b_blocks;
|
||||||
|
}
|
||||||
|
// Reconcile arrays by the equality of the elements in the array. This algorithm
|
||||||
|
// is based on Ivi's reconcilation logic:
|
||||||
|
//
|
||||||
|
// https://github.com/localvoid/ivi/blob/9f1bd0918f487da5b131941228604763c5d8ef56/packages/ivi/src/client/core.ts#L968
|
||||||
|
//
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @template V
|
||||||
|
* @param {Array<V>} array
|
||||||
|
* @param {import('./types.js').EachBlock} each_block
|
||||||
|
* @param {Element | Comment | Text} dom
|
||||||
|
* @param {boolean} is_controlled
|
||||||
|
* @param {(anchor: null, item: V, index: number | import('./types.js').Signal<number>) => void} render_fn
|
||||||
|
* @param {number} flags
|
||||||
|
* @param {boolean} apply_transitions
|
||||||
|
* @param {Array<string> | null} keys
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
export function reconcile_tracked_array(
|
||||||
|
array,
|
||||||
|
each_block,
|
||||||
|
dom,
|
||||||
|
is_controlled,
|
||||||
|
render_fn,
|
||||||
|
flags,
|
||||||
|
apply_transitions,
|
||||||
|
keys
|
||||||
|
) {
|
||||||
|
var a_blocks = each_block.v;
|
||||||
|
const is_computed_key = keys !== null;
|
||||||
|
var is_proxied_array = STATE_SYMBOL in array;
|
||||||
|
var active_transitions = each_block.s;
|
||||||
|
|
||||||
|
if (is_proxied_array) {
|
||||||
|
flags &= ~EACH_ITEM_REACTIVE;
|
||||||
|
flags |= EACH_IS_PROXIED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @type {number | void} */
|
||||||
|
var a = a_blocks.length;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var b = array.length;
|
||||||
|
|
||||||
|
/** @type {Array<import('./types.js').EachItemBlock>} */
|
||||||
|
var b_blocks;
|
||||||
|
var block;
|
||||||
|
if (active_transitions.length !== 0) {
|
||||||
|
destroy_active_transition_blocks(active_transitions);
|
||||||
|
}
|
||||||
|
if (b === 0) {
|
||||||
|
b_blocks = [];
|
||||||
|
// Remove old blocks
|
||||||
|
if (is_controlled && a !== 0) {
|
||||||
|
clear_text_content(dom);
|
||||||
|
}
|
||||||
|
while (a > 0) {
|
||||||
|
block = a_blocks[--a];
|
||||||
|
destroy_each_item_block(block, active_transitions, apply_transitions, is_controlled);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var a_end = a - 1;
|
||||||
|
var b_end = b - 1;
|
||||||
|
var key;
|
||||||
|
var item;
|
||||||
|
var idx;
|
||||||
|
b_blocks = Array(b);
|
||||||
|
if (current_hydration_fragment !== null) {
|
||||||
|
var fragment;
|
||||||
|
|
||||||
|
/** @type {Node} */
|
||||||
|
var hydrating_node = current_hydration_fragment[0];
|
||||||
|
while (b > 0) {
|
||||||
|
// Hydrate block
|
||||||
|
idx = b_end - --b;
|
||||||
|
item = array[idx];
|
||||||
|
key = is_computed_key ? keys[idx] : item;
|
||||||
|
fragment = /** @type {Array<Text | Comment | Element>} */ (
|
||||||
|
get_hydration_fragment(hydrating_node)
|
||||||
|
);
|
||||||
|
set_current_hydration_fragment(fragment);
|
||||||
|
// Get the <!--ssr:..--> tag of the next item in the list
|
||||||
|
// The fragment array can be empty if each block has no content
|
||||||
|
hydrating_node = /** @type {Node} */ (
|
||||||
|
/** @type {Node} */ ((fragment.at(-1) || hydrating_node).nextSibling).nextSibling
|
||||||
|
);
|
||||||
|
block = each_item_block(array, item, key, idx, render_fn, flags);
|
||||||
|
b_blocks[idx] = block;
|
||||||
|
}
|
||||||
|
} else if (a === 0) {
|
||||||
|
// Create new blocks
|
||||||
|
while (b > 0) {
|
||||||
|
idx = b_end - --b;
|
||||||
|
item = array[idx];
|
||||||
|
key = is_computed_key ? keys[idx] : item;
|
||||||
|
block = each_item_block(array, item, key, idx, render_fn, flags);
|
||||||
|
b_blocks[idx] = block;
|
||||||
|
insert_each_item_block(block, dom, is_controlled, null);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var should_update_block = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
|
||||||
|
var start = 0;
|
||||||
|
|
||||||
|
/** @type {null | Text | Element | Comment} */
|
||||||
|
var sibling = null;
|
||||||
|
item = array[b_end];
|
||||||
|
key = is_computed_key ? keys[b_end] : item;
|
||||||
|
// Step 1
|
||||||
|
outer: while (true) {
|
||||||
|
// From the end
|
||||||
|
while (a_blocks[a_end].k === key) {
|
||||||
|
block = a_blocks[a_end--];
|
||||||
|
item = array[b_end];
|
||||||
|
if (should_update_block) {
|
||||||
|
update_each_item_block(block, item, b_end, flags);
|
||||||
|
}
|
||||||
|
sibling = get_first_child(block);
|
||||||
|
b_blocks[b_end] = block;
|
||||||
|
if (start > --b_end || start > a_end) {
|
||||||
|
break outer;
|
||||||
|
}
|
||||||
|
key = is_computed_key ? keys[b_end] : item;
|
||||||
|
}
|
||||||
|
item = array[start];
|
||||||
|
key = is_computed_key ? keys[start] : item;
|
||||||
|
// At the start
|
||||||
|
while (start <= a_end && start <= b_end && a_blocks[start].k === key) {
|
||||||
|
item = array[start];
|
||||||
|
block = a_blocks[start];
|
||||||
|
if (should_update_block) {
|
||||||
|
update_each_item_block(block, item, start, flags);
|
||||||
|
}
|
||||||
|
b_blocks[start] = block;
|
||||||
|
++start;
|
||||||
|
key = is_computed_key ? keys[start] : array[start];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Step 2
|
||||||
|
if (start > a_end) {
|
||||||
|
while (b_end >= start) {
|
||||||
|
item = array[b_end];
|
||||||
|
key = is_computed_key ? keys[b_end] : item;
|
||||||
|
block = each_item_block(array, item, key, b_end, render_fn, flags);
|
||||||
|
b_blocks[b_end--] = block;
|
||||||
|
sibling = insert_each_item_block(block, dom, is_controlled, sibling);
|
||||||
|
}
|
||||||
|
} else if (start > b_end) {
|
||||||
|
b = start;
|
||||||
|
do {
|
||||||
|
if ((block = a_blocks[b++]) !== null) {
|
||||||
|
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||||
|
}
|
||||||
|
} while (b <= a_end);
|
||||||
|
} else {
|
||||||
|
// Step 3
|
||||||
|
var pos = 0;
|
||||||
|
var b_length = b_end - start + 1;
|
||||||
|
var sources = new Int32Array(b_length);
|
||||||
|
var item_index = new Map();
|
||||||
|
for (b = 0; b < b_length; ++b) {
|
||||||
|
a = b + start;
|
||||||
|
sources[b] = NEW_BLOCK;
|
||||||
|
item = array[a];
|
||||||
|
key = is_computed_key ? keys[a] : item;
|
||||||
|
map_set(item_index, key, a);
|
||||||
|
}
|
||||||
|
for (b = start; b <= a_end; ++b) {
|
||||||
|
a = map_get(item_index, /** @type {V} */ (a_blocks[b].k));
|
||||||
|
block = a_blocks[b];
|
||||||
|
if (a !== undefined) {
|
||||||
|
pos = pos < a ? a : MOVED_BLOCK;
|
||||||
|
sources[a - start] = b;
|
||||||
|
b_blocks[a] = block;
|
||||||
|
} else if (block !== null) {
|
||||||
|
destroy_each_item_block(block, active_transitions, apply_transitions);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 4
|
||||||
|
if (pos === MOVED_BLOCK) {
|
||||||
|
mark_lis(sources);
|
||||||
|
}
|
||||||
|
// If keys are animated, we need to do updates before actual moves
|
||||||
|
var is_animated = (flags & EACH_IS_ANIMATED) !== 0;
|
||||||
|
var should_create;
|
||||||
|
if (is_animated) {
|
||||||
|
var i = b_length;
|
||||||
|
while (i-- > 0) {
|
||||||
|
b_end = i + start;
|
||||||
|
a = sources[i];
|
||||||
|
if (pos === MOVED_BLOCK && a !== LIS_BLOCK) {
|
||||||
|
block = b_blocks[b_end];
|
||||||
|
update_each_item_block(block, item, b_end, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var last_block;
|
||||||
|
var last_sibling;
|
||||||
|
while (b_length-- > 0) {
|
||||||
|
b_end = b_length + start;
|
||||||
|
a = sources[b_length];
|
||||||
|
should_create = a === -1;
|
||||||
|
item = array[b_end];
|
||||||
|
if (should_create) {
|
||||||
|
key = is_computed_key ? keys[b_end] : item;
|
||||||
|
block = each_item_block(array, item, key, b_end, render_fn, flags);
|
||||||
|
} else {
|
||||||
|
block = b_blocks[b_end];
|
||||||
|
if (!is_animated && should_update_block) {
|
||||||
|
update_each_item_block(block, item, b_end, flags);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (should_create || (pos === MOVED_BLOCK && a !== LIS_BLOCK)) {
|
||||||
|
last_sibling = last_block === undefined ? sibling : get_first_child(last_block);
|
||||||
|
sibling = insert_each_item_block(block, dom, is_controlled, last_sibling);
|
||||||
|
}
|
||||||
|
b_blocks[b_end] = block;
|
||||||
|
last_block = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
each_block.v = b_blocks;
|
||||||
|
}
|
||||||
|
// Longest Increased Subsequence algorithm.
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Int32Array} a
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function mark_lis(a) {
|
||||||
|
var length = a.length;
|
||||||
|
var parent = new Int32Array(length);
|
||||||
|
var index = new Int32Array(length);
|
||||||
|
var index_length = 0;
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var j;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var k;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var lo;
|
||||||
|
|
||||||
|
/** @type {number} */
|
||||||
|
var hi;
|
||||||
|
// Skip -1 values at the start of the input array `a`.
|
||||||
|
for (; a[i] === NEW_BLOCK; ++i) {
|
||||||
|
/**/
|
||||||
|
}
|
||||||
|
index[0] = i++;
|
||||||
|
for (; i < length; ++i) {
|
||||||
|
k = a[i];
|
||||||
|
if (k !== NEW_BLOCK) {
|
||||||
|
// Ignore -1 values.
|
||||||
|
j = index[index_length];
|
||||||
|
if (a[j] < k) {
|
||||||
|
parent[i] = j;
|
||||||
|
index[++index_length] = i;
|
||||||
|
} else {
|
||||||
|
lo = 0;
|
||||||
|
hi = index_length;
|
||||||
|
while (lo < hi) {
|
||||||
|
j = (lo + hi) >> 1;
|
||||||
|
if (a[index[j]] < k) {
|
||||||
|
lo = j + 1;
|
||||||
|
} else {
|
||||||
|
hi = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k < a[index[lo]]) {
|
||||||
|
if (lo > 0) {
|
||||||
|
parent[i] = index[lo - 1];
|
||||||
|
}
|
||||||
|
index[lo] = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Mutate input array `a` and assign -2 value to all nodes that are part of LIS.
|
||||||
|
j = index[index_length];
|
||||||
|
while (index_length-- >= 0) {
|
||||||
|
a[j] = LIS_BLOCK;
|
||||||
|
j = parent[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types.js').Block} block
|
||||||
|
* @param {Element | Comment | Text} dom
|
||||||
|
* @param {boolean} is_controlled
|
||||||
|
* @param {null | Text | Element | Comment} sibling
|
||||||
|
* @returns {Text | Element | Comment}
|
||||||
|
*/
|
||||||
|
function insert_each_item_block(block, dom, is_controlled, sibling) {
|
||||||
|
var current = /** @type {import('./types.js').TemplateNode} */ (block.d);
|
||||||
|
if (sibling === null) {
|
||||||
|
if (is_controlled) {
|
||||||
|
return insert(current, /** @type {Element} */ (dom), null);
|
||||||
|
} else {
|
||||||
|
return insert(current, /** @type {Element} */ (dom.parentNode), dom);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return insert(current, null, sibling);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./types.js').Block} block
|
||||||
|
* @returns {Text | Element | Comment}
|
||||||
|
*/
|
||||||
|
function get_first_child(block) {
|
||||||
|
var current = block.d;
|
||||||
|
if (is_array(current)) {
|
||||||
|
return /** @type {Text | Element | Comment} */ (current[0]);
|
||||||
|
}
|
||||||
|
return /** @type {Text | Element | Comment} */ (current);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Array<import('./types.js').EachItemBlock>} active_transitions
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function destroy_active_transition_blocks(active_transitions) {
|
||||||
|
var length = active_transitions.length;
|
||||||
|
if (length > 0) {
|
||||||
|
var i = 0;
|
||||||
|
var block;
|
||||||
|
var transition;
|
||||||
|
for (; i < length; i++) {
|
||||||
|
block = active_transitions[i];
|
||||||
|
transition = block.r;
|
||||||
|
if (transition !== null) {
|
||||||
|
block.r = null;
|
||||||
|
destroy_each_item_block(block, null, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
active_transitions.length = 0;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue