Compare commits

...

2 Commits

Author SHA1 Message Date
Lea Verou
82faf7da36 Don't break in SSR 2025-02-10 10:20:50 -05:00
Lea Verou
fd0fad361f <wa-isolate> as a wrapper for DSD 2025-02-10 10:17:23 -05:00
5 changed files with 73 additions and 25 deletions

View File

@@ -1,7 +1,7 @@
{% set themeId = theme.fileSlug %}
<div>
<template shadowrootmode="open">
<wa-isolate>
<template>
<link rel="stylesheet" href="/dist/styles/utilities.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ themeId }}/color.css">
@@ -21,4 +21,4 @@
{# <div class="wa-warning wa-outlined wa-filled"><wa-icon slot="icon" name="triangle-exclamation" variant="regular"></wa-icon></div> #}
</div>
</template>
</div>
</wa-isolate>

View File

@@ -1,7 +1,7 @@
{% set themeId = theme.fileSlug %}
<div>
<template shadowrootmode="open">
<wa-isolate>
<template>
<link rel="stylesheet" href="/dist/styles/native/content.css">
<link rel="stylesheet" href="/dist/styles/native/blockquote.css">
<link rel="stylesheet" href="/dist/styles/themes/{{ page.fileSlug or 'default' }}.css">
@@ -13,4 +13,4 @@
<p>Body text</p>
</div>
</template>
</div>
</wa-isolate>

View File

@@ -1,6 +1,7 @@
import { html } from 'lit';
import { customElement, property, query } from 'lit/decorators.js';
import { classMap } from 'lit/directives/class-map.js';
import recursiveQSA from '../../internal/recursive-qsa.js';
import { HasSlotController, getInnerHTML } from '../../internal/slot.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import '../icon/icon.js';
@@ -325,25 +326,6 @@ function absolutizeURLs(root: Element | DocumentFragment, base = location.href)
}
}
/**
* Get elements that match a selector within an elements shadow tree
* and any parent shadow trees, all the way up to the light DOM
* @param selector
* @param node - The node to start the search from
*/
function recursiveQSA(selector: string, node: Node) {
const ret: Element[] = [];
for (let root = node; root.nodeType !== Node.DOCUMENT_NODE; ) {
root = root.getRootNode();
const elements = (root as ShadowRoot | Document).querySelectorAll(selector);
ret.push(...elements);
}
return ret;
}
function dedent(code: string) {
// Remove blank lines at the start and end
code = code.replace(/^\s*\n|\n\s*$/g, '');

View File

@@ -0,0 +1,48 @@
import { customElement, property } from 'lit/decorators.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import recursiveQSA from '../../internal/recursive-qsa.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
/**
* @summary Render a piece of code in shadow DOM. An alternative to DSD that plays nicely with DOM operations like cloning etc.
* @documentation https://backers.webawesome.com/docs/components/isolate
* @status experimental
* @since 3.0
*
*/
@customElement('wa-isolate')
export default class WaIsolate extends WebAwesomeElement {
/** Includes resources and other elements from the surrounding page */
@property({ reflect: true }) include?: string;
render() {
return unsafeHTML(this.getShadowHTML());
}
// TODO memoize this and only update if:
// - this.include changes
// - elements have been added/removed that match the selector
// - Element contents have changed
private getShadowHTML(): string {
let html = '';
if (this.ownerDocument) {
if (this.include) {
let includedElements = recursiveQSA(this.include, this);
html += includedElements.map(el => el.outerHTML).join('\n');
}
// Get template elements from the light DOM
const templates = Array.from(this.querySelectorAll(':scope > template'));
html += templates.map(template => template.innerHTML).join('\n');
}
return html;
}
}
declare global {
interface HTMLElementTagNameMap {
'wa-isolate': WaIsolate;
}
}

View File

@@ -0,0 +1,18 @@
/**
* Get elements that match a selector within an elements shadow tree
* and any parent shadow trees, all the way up to the light DOM
* @param selector
* @param node - The node to start the search from
*/
export default function recursiveQSA(selector: string, node: Node) {
const ret: Element[] = [];
for (let root = node; root.nodeType !== Node.DOCUMENT_NODE; ) {
root = root.getRootNode();
const elements = (root as ShadowRoot | Document).querySelectorAll(selector);
ret.push(...elements);
}
return ret;
}