|
|
|
@ -6,6 +6,7 @@ import {
|
|
|
|
branch,
|
|
|
|
branch,
|
|
|
|
destroy_effect,
|
|
|
|
destroy_effect,
|
|
|
|
pause_effect,
|
|
|
|
pause_effect,
|
|
|
|
|
|
|
|
render_effect,
|
|
|
|
resume_effect
|
|
|
|
resume_effect
|
|
|
|
} from '../../reactivity/effects.js';
|
|
|
|
} from '../../reactivity/effects.js';
|
|
|
|
import { remove } from '../reconciler.js';
|
|
|
|
import { remove } from '../reconciler.js';
|
|
|
|
@ -45,110 +46,105 @@ function swap_block_dom(effect, from, to) {
|
|
|
|
export function element(anchor, get_tag, is_svg, render_fn) {
|
|
|
|
export function element(anchor, get_tag, is_svg, render_fn) {
|
|
|
|
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
|
|
|
|
const parent_effect = /** @type {import('#client').Effect} */ (current_effect);
|
|
|
|
|
|
|
|
|
|
|
|
/** @type {string | null} */
|
|
|
|
render_effect(() => {
|
|
|
|
let tag;
|
|
|
|
/** @type {string | null} */
|
|
|
|
|
|
|
|
let tag;
|
|
|
|
/** @type {string | null} */
|
|
|
|
|
|
|
|
let current_tag;
|
|
|
|
/** @type {string | null} */
|
|
|
|
|
|
|
|
let current_tag;
|
|
|
|
/** @type {null | Element} */
|
|
|
|
|
|
|
|
let element = null;
|
|
|
|
/** @type {null | Element} */
|
|
|
|
|
|
|
|
let element = null;
|
|
|
|
/** @type {import('#client').Effect | null} */
|
|
|
|
|
|
|
|
let effect;
|
|
|
|
/** @type {import('#client').Effect | null} */
|
|
|
|
|
|
|
|
let effect;
|
|
|
|
/**
|
|
|
|
|
|
|
|
* The keyed `{#each ...}` item block, if any, that this element is inside.
|
|
|
|
/**
|
|
|
|
* We track this so we can set it when changing the element, allowing any
|
|
|
|
* The keyed `{#each ...}` item block, if any, that this element is inside.
|
|
|
|
* `animate:` directive to bind itself to the correct block
|
|
|
|
* We track this so we can set it when changing the element, allowing any
|
|
|
|
*/
|
|
|
|
* `animate:` directive to bind itself to the correct block
|
|
|
|
let each_item_block = current_each_item;
|
|
|
|
*/
|
|
|
|
|
|
|
|
let each_item_block = current_each_item;
|
|
|
|
const wrapper = block(() => {
|
|
|
|
|
|
|
|
const next_tag = get_tag() || null;
|
|
|
|
block(() => {
|
|
|
|
if (next_tag === tag) return;
|
|
|
|
const next_tag = get_tag() || null;
|
|
|
|
|
|
|
|
if (next_tag === tag) return;
|
|
|
|
// See explanation of `each_item_block` above
|
|
|
|
|
|
|
|
var previous_each_item = current_each_item;
|
|
|
|
// See explanation of `each_item_block` above
|
|
|
|
set_current_each_item(each_item_block);
|
|
|
|
var previous_each_item = current_each_item;
|
|
|
|
|
|
|
|
set_current_each_item(each_item_block);
|
|
|
|
// We try our best infering the namespace in case it's not possible to determine statically,
|
|
|
|
|
|
|
|
// but on the first render on the client (without hydration) the parent will be undefined,
|
|
|
|
// We try our best infering the namespace in case it's not possible to determine statically,
|
|
|
|
// since the anchor is not attached to its parent / the dom yet.
|
|
|
|
// but on the first render on the client (without hydration) the parent will be undefined,
|
|
|
|
const ns =
|
|
|
|
// since the anchor is not attached to its parent / the dom yet.
|
|
|
|
is_svg || next_tag === 'svg'
|
|
|
|
const ns =
|
|
|
|
? namespace_svg
|
|
|
|
is_svg || next_tag === 'svg'
|
|
|
|
: is_svg === false || anchor.parentElement?.tagName === 'foreignObject'
|
|
|
|
? namespace_svg
|
|
|
|
? null
|
|
|
|
: is_svg === false || anchor.parentElement?.tagName === 'foreignObject'
|
|
|
|
: anchor.parentElement?.namespaceURI ?? null;
|
|
|
|
? null
|
|
|
|
|
|
|
|
: anchor.parentElement?.namespaceURI ?? null;
|
|
|
|
if (effect) {
|
|
|
|
|
|
|
|
if (next_tag === null) {
|
|
|
|
if (effect) {
|
|
|
|
// start outro
|
|
|
|
if (next_tag === null) {
|
|
|
|
pause_effect(effect, () => {
|
|
|
|
// start outro
|
|
|
|
effect = null;
|
|
|
|
pause_effect(effect, () => {
|
|
|
|
current_tag = null;
|
|
|
|
effect = null;
|
|
|
|
element?.remove(); // TODO this should be unnecessary
|
|
|
|
current_tag = null;
|
|
|
|
});
|
|
|
|
element?.remove(); // TODO this should be unnecessary
|
|
|
|
} else if (next_tag === current_tag) {
|
|
|
|
});
|
|
|
|
// same tag as is currently rendered — abort outro
|
|
|
|
} else if (next_tag === current_tag) {
|
|
|
|
resume_effect(effect);
|
|
|
|
// same tag as is currently rendered — abort outro
|
|
|
|
} else {
|
|
|
|
resume_effect(effect);
|
|
|
|
// tag is changing — destroy immediately, render contents without intro transitions
|
|
|
|
} else {
|
|
|
|
destroy_effect(effect);
|
|
|
|
// tag is changing — destroy immediately, render contents without intro transitions
|
|
|
|
set_should_intro(false);
|
|
|
|
destroy_effect(effect);
|
|
|
|
|
|
|
|
set_should_intro(false);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (next_tag && next_tag !== current_tag) {
|
|
|
|
if (next_tag && next_tag !== current_tag) {
|
|
|
|
effect = branch(() => {
|
|
|
|
effect = branch(() => {
|
|
|
|
const prev_element = element;
|
|
|
|
const prev_element = element;
|
|
|
|
element = hydrating
|
|
|
|
element = hydrating
|
|
|
|
? /** @type {Element} */ (hydrate_nodes[0])
|
|
|
|
? /** @type {Element} */ (hydrate_nodes[0])
|
|
|
|
: ns
|
|
|
|
: ns
|
|
|
|
? document.createElementNS(ns, next_tag)
|
|
|
|
? document.createElementNS(ns, next_tag)
|
|
|
|
: document.createElement(next_tag);
|
|
|
|
: document.createElement(next_tag);
|
|
|
|
|
|
|
|
|
|
|
|
if (render_fn) {
|
|
|
|
if (render_fn) {
|
|
|
|
// If hydrating, use the existing ssr comment as the anchor so that the
|
|
|
|
// If hydrating, use the existing ssr comment as the anchor so that the
|
|
|
|
// inner open and close methods can pick up the existing nodes correctly
|
|
|
|
// inner open and close methods can pick up the existing nodes correctly
|
|
|
|
var child_anchor = hydrating
|
|
|
|
var child_anchor = hydrating
|
|
|
|
? element.firstChild && hydrate_anchor(/** @type {Comment} */ (element.firstChild))
|
|
|
|
? element.firstChild && hydrate_anchor(/** @type {Comment} */ (element.firstChild))
|
|
|
|
: element.appendChild(empty());
|
|
|
|
: element.appendChild(empty());
|
|
|
|
|
|
|
|
|
|
|
|
if (child_anchor) {
|
|
|
|
if (child_anchor) {
|
|
|
|
// `child_anchor` can be undefined if this is a void element with children,
|
|
|
|
// `child_anchor` can be undefined if this is a void element with children,
|
|
|
|
// i.e. `<svelte:element this={"hr"}>...</svelte:element>`. This is
|
|
|
|
// i.e. `<svelte:element this={"hr"}>...</svelte:element>`. This is
|
|
|
|
// user error, but we warn on it elsewhere (in dev) so here we just
|
|
|
|
// user error, but we warn on it elsewhere (in dev) so here we just
|
|
|
|
// silently ignore it
|
|
|
|
// silently ignore it
|
|
|
|
render_fn(element, child_anchor);
|
|
|
|
render_fn(element, child_anchor);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
anchor.before(element);
|
|
|
|
anchor.before(element);
|
|
|
|
|
|
|
|
|
|
|
|
if (prev_element) {
|
|
|
|
|
|
|
|
swap_block_dom(parent_effect, prev_element, element);
|
|
|
|
|
|
|
|
prev_element.remove();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tag = next_tag;
|
|
|
|
if (prev_element) {
|
|
|
|
if (tag) current_tag = tag;
|
|
|
|
swap_block_dom(parent_effect, prev_element, element);
|
|
|
|
set_should_intro(true);
|
|
|
|
prev_element.remove();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
set_current_each_item(previous_each_item);
|
|
|
|
tag = next_tag;
|
|
|
|
});
|
|
|
|
if (tag) current_tag = tag;
|
|
|
|
|
|
|
|
set_should_intro(true);
|
|
|
|
|
|
|
|
|
|
|
|
wrapper.ondestroy = () => {
|
|
|
|
set_current_each_item(previous_each_item);
|
|
|
|
if (element !== null) {
|
|
|
|
});
|
|
|
|
remove(element);
|
|
|
|
|
|
|
|
element = null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (effect) {
|
|
|
|
return () => {
|
|
|
|
destroy_effect(effect);
|
|
|
|
element?.remove();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|