We can't find the internet
Attempting to reconnect
## Concepts
Class `CustomElementRegistry`
* `define`
The define() method of the [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) interface defines a new custom element.
There are two types of custom elements you can create:
* Autonomous custom element: Standalone elements; they don’t inherit from built-in HTML elements.
* Customized built-in element: These elements inherit from — and extend — built-in HTML elements.
```
customElements.define(name, constructor, options);
```
* `upgrade`
The upgrade() method of the [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) interface upgrades all shadow-containing custom elements in a [Node](https://developer.mozilla.org/en-US/docs/Web/API/Node) subtree, even before they are connected to the main document.
```
customElements.upgrade(root);
```
* `whenDefined`
The whenDefined() method of the [CustomElementRegistry](https://developer.mozilla.org/en-US/docs/Web/API/CustomElementRegistry) interface returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise) that resolves when the named element is defined.
```
customElements.whenDefined(name): Promise<CustomElementConstructor>;
```
## Example
```javascript
// Create a class for the element
class WordCount extends HTMLParagraphElement {
constructor() {
// Always call super first in constructor
super();
// count words in element's parent element
var wcParent = this.parentNode;
function countWords(node){
var text = node.innerText || node.textContent
return text.split(/\s+/g).length;
}
var count = 'Words: ' + countWords(wcParent);
// Create a shadow root
var shadow = this.attachShadow({mode: 'open'});
// Create text node and add word count to it
var text = document.createElement('span');
text.textContent = count;
// Append it to the shadow root
shadow.appendChild(text);
// Update count when element content changes
setInterval(function() {
var count = 'Words: ' + countWords(wcParent);
text.textContent = count;
}, 200)
}
}
// Define the new element
customElements.define('word-count', WordCount, { extends: 'p' });
```
## Real World Example
```typescript
import { LitElement, html, css, unsafeCSS } from 'lit';
import { customElement, property } from 'lit/decorators.js';
import { until } from 'lit/directives/until.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { unified } from 'unified';
import remarkParse from 'remark-parse';
import remarkGfm from 'remark-gfm';
import remarkRehype from 'remark-rehype';
import rehypeStringify from 'rehype-stringify';
import rehypeHighlight from 'rehype-highlight';
import mermaid from 'mermaid';
import github from './github.css';
import light from './atom-one-light.css';
import dark from './atom-one-dark.css';
@customElement('remark-element')
class RemarkElement extends LitElement {
static override styles = css`
:host {
display: flex;
flex-direction: column;
}
li > * {
display: inline;
}
li > ul {
display: block;
}
${unsafeCSS(github)}
@media (prefers-color-scheme: dark) {
${unsafeCSS(dark)}
}
@media (prefers-color-scheme: light) {
${unsafeCSS(light)}
}
`;
@property({ type: Boolean, attribute: true, reflect: true })
debug: boolean = false;
private _content: string | undefined = undefined;
set content(val: string | undefined) {
const oldVal = this._content;
this._content = val;
this.requestUpdate('content', oldVal);
}
get content() {
return this._content;
}
private _mid: string;
private _fragement: string;
constructor() {
super();
const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
mermaid.initialize({
startOnLoad: false,
theme: isDark ? 'dark' : 'default',
});
this._mid = this.id || `t${+new Date()}`;
this._fragement = '';
}
private _generate() {
const content = this.content ?? this.innerHTML;
return unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkRehype)
.use(rehypeHighlight, { ignoreMissing: true })
.use(rehypeStringify)
.process(content)
.then((vFile) => {
this._fragement = String(vFile);
return unsafeHTML(String(vFile));
});
}
private _do_updated() {
const els: NodeListOf<HTMLElement> = this.renderRoot.querySelectorAll(
'code.language-mermaid',
);
const fragement = document.createElement('div');
fragement.innerHTML = this._fragement;
const contentEls: NodeListOf<HTMLElement> = fragement.querySelectorAll(
'code.language-mermaid',
);
for (let i = 0, len = els.length; i < len; i += 1) {
const boxId = `mermaid-${this._mid}-${i}`;
let box = document.getElementById(boxId);
if (!box) {
box = document.createElement('div');
box.id = boxId;
document.body.append(box);
box.style.display = 'none';
}
const el = els[i];
const txt = contentEls[i].innerText;
if (this.debug) {
console.log(txt);
}
const cb = (svgGraph: string) => {
if (this.debug) {
console.log(svgGraph);
}
el.innerHTML = svgGraph;
};
const decodedTxt = this._decodeEntities(txt);
if (this.debug) {
console.log(decodedTxt);
}
mermaid.mermaidAPI.render(box.id, decodedTxt, cb);
}
}
override updated() {
setTimeout(() => this._do_updated(), 1000 / 60);
}
// override attributeChangedCallback(...args) {
// if (this.debug) {
// console.log(...args);
// }
// this.requestUpdate(...args);
// }
override render() {
const md = this._generate();
return html`${until(md)}`;
}
private _decodeEntities(txt: string): string {
return txt.replace(/>/gi, '>').replace(/</gi, '<');
}
}
declare global {
interface HTMLElementTagNameMap {
'remark-element': RemarkElement;
}
}
```
### Relative
* Find more custom element see [Awesome Lit](https://github.com/web-padawan/awesome-lit)