@downforce/webx
Version:
Awesome Web Components and Custom Elements
78 lines (61 loc) • 1.82 kB
text/typescript
import {tryOrValue} from '@downforce/std/fn-try'
import {onMounted, useEventListener, WebElement} from '@downforce/web/element'
/*
* EXAMPLE
*
* customElements.define('html-sandbox', HtmlSandbox)
*
* <html-sandbox>
* <style>
* p { color: red; }
* </style>
*
* <p>Hello World!</p>
* </html-sandbox>
*/
export class HtmlSandbox extends WebElement {
constructor() {
super()
this.attachShadow({mode: 'open'})
onMounted(this, () => {
const observer = new MutationObserver(() => onContentChange(this))
observer.observe(this, {subtree: true, characterData: true})
function onUnmount() {
observer.disconnect()
}
return onUnmount
})
useEventListener(this, window, 'hashchange', onHashChange.bind(this, this))
}
override connectedCallback(): void {
onContentChange(this)
}
}
export function onContentChange(element: HTMLElement): void {
if (! element.shadowRoot) {
return
}
const children = Array.from(element.childNodes)
const html = children.map(it => it.textContent).join('\n')
element.shadowRoot.innerHTML = html
onHashChange(element)
}
export function onHashChange(element: HTMLElement): void {
const target = tryOrValue(() => {
if (! element.shadowRoot) {
return
}
if (! window.location.hash) {
return
}
const id = window.location.hash
const shadowElement = element.shadowRoot.querySelector<HTMLElement>(id)
return shadowElement
}, undefined)
if (! target) {
return
}
// We can't use {behavior:'smooth'} because the Safari shim/polyfill
// does not work inside Web Components.
target.scrollIntoView()
}