support css selectors on the client

portals
Simon Holthausen 2 years ago
parent 1d40d3f237
commit ae62c7c84b

@ -1,7 +1,7 @@
/** @import { TemplateNode } from '#client' */ /** @import { TemplateNode } from '#client' */
import { HYDRATION_END, HYDRATION_START, HYDRATION_START_ELSE } from '../../../../constants.js'; import { HYDRATION_END, HYDRATION_START, HYDRATION_START_ELSE } from '../../../../constants.js';
import { block, remove_nodes, render_effect } from '../../reactivity/effects.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'; import { get_next_sibling } from '../operations.js';
const portals = new Map(); const portals = new Map();
@ -44,18 +44,29 @@ export function portal_outlet(node, id) {
* @returns {void} * @returns {void}
*/ */
export function portal(target, content) { export function portal(target, content) {
const is_css_selector = typeof target === 'string';
const portal = portals.get(target); 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( throw new Error(
'TODO error code: No portal found for given target. Make sure portal target exists before referencing it' '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) { if (hydrating) {
previous_hydrate_node = hydrate_node; previous_hydrating = true;
set_hydrate_node((anchor = /** @type {TemplateNode} */ (get_next_sibling(portal.anchor)))); 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(() => { const effect = block(() => {
@ -69,8 +80,12 @@ export function portal(target, content) {
}; };
}); });
if (hydrating) { if (previous_hydrating) {
portal.anchor = hydrate_node; // so that next head block starts from the correct node if (is_css_selector) {
set_hydrate_node(/** @type {TemplateNode} */ (previous_hydrate_node)); 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));
}
} }
} }

@ -180,6 +180,10 @@ export function portal_outlet(payload, id) {
* @returns {void} * @returns {void}
*/ */
export function portal(payload, target, content) { export function portal(payload, target, content) {
payload.out += EMPTY_COMMENT;
if (typeof target === 'string') return; // targets a CSS selector - not possible in SSR, ignore
const portal = payload.portals.get(target); const portal = payload.portals.get(target);
if (!portal) if (!portal)
throw new Error( throw new Error(
@ -191,7 +195,6 @@ export function portal(payload, target, content) {
content(tmp_payload); content(tmp_payload);
tmp_payload.out += EMPTY_COMMENT; tmp_payload.out += EMPTY_COMMENT;
portal.content.push(tmp_payload.out); portal.content.push(tmp_payload.out);
payload.out += EMPTY_COMMENT;
} }
/** /**

Loading…
Cancel
Save