UNPKG

svelte

Version:

Cybernetically enhanced web apps

166 lines (139 loc) 4.72 kB
/** @import { Effect, TemplateNode } from '#client' */ import { FILENAME, NAMESPACE_SVG } from '../../../../constants.js'; import { hydrate_next, hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js'; import { create_text, get_first_child } from '../operations.js'; import { block, branch, destroy_effect, pause_effect, resume_effect } from '../../reactivity/effects.js'; import { set_should_intro } from '../../render.js'; import { current_each_item, set_current_each_item } from './each.js'; import { active_effect } from '../../runtime.js'; import { component_context } from '../../context.js'; import { DEV } from 'esm-env'; import { EFFECT_TRANSPARENT } from '../../constants.js'; import { assign_nodes } from '../template.js'; import { is_raw_text_element } from '../../../../utils.js'; /** * @param {Comment | Element} node * @param {() => string} get_tag * @param {boolean} is_svg * @param {undefined | ((element: Element, anchor: Node | null) => void)} render_fn, * @param {undefined | (() => string)} get_namespace * @param {undefined | [number, number]} location * @returns {void} */ export function element(node, get_tag, is_svg, render_fn, get_namespace, location) { let was_hydrating = hydrating; if (hydrating) { hydrate_next(); } var filename = DEV && location && component_context?.function[FILENAME]; /** @type {string | null} */ var tag; /** @type {string | null} */ var current_tag; /** @type {null | Element} */ var element = null; if (hydrating && hydrate_node.nodeType === 1) { element = /** @type {Element} */ (hydrate_node); hydrate_next(); } var anchor = /** @type {TemplateNode} */ (hydrating ? hydrate_node : node); /** @type {Effect | null} */ var 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 * `animate:` directive to bind itself to the correct block */ var each_item_block = current_each_item; block(() => { const next_tag = get_tag() || null; var ns = get_namespace ? get_namespace() : is_svg || next_tag === 'svg' ? NAMESPACE_SVG : null; // Assumption: Noone changes the namespace but not the tag (what would that even mean?) if (next_tag === tag) return; // See explanation of `each_item_block` above var previous_each_item = current_each_item; set_current_each_item(each_item_block); if (effect) { if (next_tag === null) { // start outro pause_effect(effect, () => { effect = null; current_tag = null; }); } else if (next_tag === current_tag) { // same tag as is currently rendered — abort outro resume_effect(effect); } else { // tag is changing — destroy immediately, render contents without intro transitions destroy_effect(effect); set_should_intro(false); } } if (next_tag && next_tag !== current_tag) { effect = branch(() => { element = hydrating ? /** @type {Element} */ (element) : ns ? document.createElementNS(ns, next_tag) : document.createElement(next_tag); if (DEV && location) { // @ts-expect-error element.__svelte_meta = { loc: { file: filename, line: location[0], column: location[1] } }; } assign_nodes(element, element); if (render_fn) { if (hydrating && is_raw_text_element(next_tag)) { // prevent hydration glitches element.append(document.createComment('')); } // 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 var child_anchor = /** @type {TemplateNode} */ ( hydrating ? get_first_child(element) : element.appendChild(create_text()) ); if (hydrating) { if (child_anchor === null) { set_hydrating(false); } else { set_hydrate_node(child_anchor); } } // `child_anchor` is undefined if this is a void element, but we still // need to call `render_fn` in order to run actions etc. If the element // contains children, it's a user error (which is warned on elsewhere) // and the DOM will be silently discarded render_fn(element, child_anchor); } // we do this after calling `render_fn` so that child effects don't override `nodes.end` /** @type {Effect} */ (active_effect).nodes_end = element; anchor.before(element); }); } tag = next_tag; if (tag) current_tag = tag; set_should_intro(true); set_current_each_item(previous_each_item); }, EFFECT_TRANSPARENT); if (was_hydrating) { set_hydrating(true); set_hydrate_node(anchor); } }