pull/10948/head
Rich Harris 2 years ago
parent 9ba60e5330
commit c08bd16103

@ -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();
} };
}; });
} }

@ -12,6 +12,9 @@ export function head(render_fn) {
let previous_hydrate_nodes = null; let previous_hydrate_nodes = null;
let was_hydrating = hydrating; let was_hydrating = hydrating;
/** @type {Comment | Text} */
var anchor;
if (hydrating) { if (hydrating) {
previous_hydrate_nodes = hydrate_nodes; previous_hydrate_nodes = hydrate_nodes;
@ -21,10 +24,10 @@ export function head(render_fn) {
} }
anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(anchor)); anchor = /** @type {import('#client').TemplateNode} */ (hydrate_anchor(anchor));
} else {
anchor = document.head.appendChild(empty());
} }
var anchor = document.head.appendChild(empty());
try { try {
block(() => render_fn(anchor)); block(() => render_fn(anchor));
} finally { } finally {

Loading…
Cancel
Save