|
|
|
|
@ -1,7 +1,7 @@
|
|
|
|
|
/** @import { TemplateNode } from '#client' */
|
|
|
|
|
import { HYDRATION_END, HYDRATION_START, HYDRATION_START_ELSE } from '../../../../constants.js';
|
|
|
|
|
import { block, remove_nodes, render_effect } from '../../reactivity/effects.js';
|
|
|
|
|
import { hydrate_node, hydrating, set_hydrate_node } from '../hydration.js';
|
|
|
|
|
import { hydrate_node, hydrating, set_hydrate_node, set_hydrating } from '../hydration.js';
|
|
|
|
|
import { get_next_sibling } from '../operations.js';
|
|
|
|
|
|
|
|
|
|
const portals = new Map();
|
|
|
|
|
@ -44,18 +44,29 @@ export function portal_outlet(node, id) {
|
|
|
|
|
* @returns {void}
|
|
|
|
|
*/
|
|
|
|
|
export function portal(target, content) {
|
|
|
|
|
const is_css_selector = typeof target === 'string';
|
|
|
|
|
const portal = portals.get(target);
|
|
|
|
|
if (!portal)
|
|
|
|
|
|
|
|
|
|
let previous_hydrating = false;
|
|
|
|
|
let previous_hydrate_node = null;
|
|
|
|
|
// TODO right now the portal is rendered before the given anchor. Is that confusing for people and they'd rather have it as
|
|
|
|
|
// the first child _inside_ the anchor if the anchor is an element?
|
|
|
|
|
var anchor = is_css_selector ? document.querySelector(target) : portal?.anchor;
|
|
|
|
|
|
|
|
|
|
if (!anchor)
|
|
|
|
|
throw new Error(
|
|
|
|
|
'TODO error code: No portal found for given target. Make sure portal target exists before referencing it'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let previous_hydrate_node = null;
|
|
|
|
|
var anchor = portal.anchor;
|
|
|
|
|
|
|
|
|
|
if (hydrating) {
|
|
|
|
|
previous_hydrate_node = hydrate_node;
|
|
|
|
|
set_hydrate_node((anchor = /** @type {TemplateNode} */ (get_next_sibling(portal.anchor))));
|
|
|
|
|
previous_hydrating = true;
|
|
|
|
|
if (is_css_selector) {
|
|
|
|
|
// These are not SSR'd, so temporarily disable hydration to properly insert them
|
|
|
|
|
set_hydrating(false);
|
|
|
|
|
} else {
|
|
|
|
|
previous_hydrate_node = hydrate_node;
|
|
|
|
|
set_hydrate_node((anchor = /** @type {TemplateNode} */ (get_next_sibling(portal.anchor))));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const effect = block(() => {
|
|
|
|
|
@ -69,8 +80,12 @@ export function portal(target, content) {
|
|
|
|
|
};
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (hydrating) {
|
|
|
|
|
portal.anchor = hydrate_node; // so that next head block starts from the correct node
|
|
|
|
|
set_hydrate_node(/** @type {TemplateNode} */ (previous_hydrate_node));
|
|
|
|
|
if (previous_hydrating) {
|
|
|
|
|
if (is_css_selector) {
|
|
|
|
|
set_hydrating(true);
|
|
|
|
|
} else {
|
|
|
|
|
portal.anchor = hydrate_node; // so that next head block starts from the correct node
|
|
|
|
|
set_hydrate_node(/** @type {TemplateNode} */ (previous_hydrate_node));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|