feat: use shadow mode (#39)
This commit is contained in:
parent
d4dcea4503
commit
29fa864ca2
4 changed files with 83 additions and 5 deletions
13
examples/use-shadow/index.html
Normal file
13
examples/use-shadow/index.html
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>WC demo</title>
|
||||||
|
<script type="module" src="./index.js"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>With our html</h2>
|
||||||
|
<my-counter></my-counter>
|
||||||
|
</body>
|
||||||
|
</html>
|
55
examples/use-shadow/index.js
Normal file
55
examples/use-shadow/index.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
// @ts-check
|
||||||
|
import { WebComponent, html } from "../../src/index.js";
|
||||||
|
|
||||||
|
export class Counter extends WebComponent {
|
||||||
|
static props = {
|
||||||
|
count: 123,
|
||||||
|
};
|
||||||
|
static shadowRootInit = {
|
||||||
|
mode: "closed",
|
||||||
|
};
|
||||||
|
|
||||||
|
get template() {
|
||||||
|
const list = ["a", "b", "c", "what"];
|
||||||
|
const links = [
|
||||||
|
{
|
||||||
|
url: "https://ayco.io",
|
||||||
|
text: "Ayo Ayco",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
url: "https://ayco.io/gh/McFly",
|
||||||
|
text: "McFly",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<button
|
||||||
|
class="hey"
|
||||||
|
id="btn"
|
||||||
|
onClick=${() => ++this.props.count}
|
||||||
|
style=${{ backgroundColor: "green", color: "white" }}
|
||||||
|
about="Elephant"
|
||||||
|
data-name="thing"
|
||||||
|
aria-name="thingz"
|
||||||
|
>
|
||||||
|
<span>${this.props.count}</span>
|
||||||
|
</button>
|
||||||
|
<form style="margin: 1em 0;">
|
||||||
|
<label data-my-name="Ayo" for="the-input">Name</label>
|
||||||
|
<input id="the-input" type="foo" value="Name:" />
|
||||||
|
</form>
|
||||||
|
${list.map((item) => html`<p>${item}</p>`)}
|
||||||
|
<h3 about="Elephant">Links</h3>
|
||||||
|
<ul>
|
||||||
|
${links.map(
|
||||||
|
(link) =>
|
||||||
|
html`<li>
|
||||||
|
<a href=${link.url} target="_blank">${link.text}</a>
|
||||||
|
</li>`
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
customElements.define("my-counter", Counter);
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "web-component-base",
|
"name": "web-component-base",
|
||||||
"version": "2.1.0-beta.1",
|
"version": "2.1.0-beta.2",
|
||||||
"description": "A zero-dependency & tiny JS base class for creating reactive custom elements easily",
|
"description": "A zero-dependency & tiny JS base class for creating reactive custom elements easily",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"exports": {
|
"exports": {
|
||||||
|
|
|
@ -38,6 +38,12 @@ export class WebComponent extends HTMLElement {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shadow root initialization options
|
||||||
|
* @type {ShadowRootInit}
|
||||||
|
*/
|
||||||
|
static shadowRootInit;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Read-only property containing camelCase counterparts of observed attributes.
|
* Read-only property containing camelCase counterparts of observed attributes.
|
||||||
* @see https://www.npmjs.com/package/web-component-base#prop-access
|
* @see https://www.npmjs.com/package/web-component-base#prop-access
|
||||||
|
@ -146,7 +152,7 @@ export class WebComponent extends HTMLElement {
|
||||||
throw TypeError(
|
throw TypeError(
|
||||||
`Cannot assign ${typeof value} to ${
|
`Cannot assign ${typeof value} to ${
|
||||||
typeMap[prop]
|
typeMap[prop]
|
||||||
} property (setting '${prop}' of ${meta.constructor.name})`,
|
} property (setting '${prop}' of ${meta.constructor.name})`
|
||||||
);
|
);
|
||||||
} else if (oldValue !== value) {
|
} else if (oldValue !== value) {
|
||||||
obj[prop] = value;
|
obj[prop] = value;
|
||||||
|
@ -178,7 +184,7 @@ export class WebComponent extends HTMLElement {
|
||||||
if (!this.#props) {
|
if (!this.#props) {
|
||||||
this.#props = new Proxy(
|
this.#props = new Proxy(
|
||||||
initialProps,
|
initialProps,
|
||||||
this.#handler((key, value) => this.setAttribute(key, value), this),
|
this.#handler((key, value) => this.setAttribute(key, value), this)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -188,14 +194,18 @@ export class WebComponent extends HTMLElement {
|
||||||
if (typeof this.template === "string") {
|
if (typeof this.template === "string") {
|
||||||
this.innerHTML = this.template;
|
this.innerHTML = this.template;
|
||||||
} else if (typeof this.template === "object") {
|
} else if (typeof this.template === "object") {
|
||||||
|
let host = this;
|
||||||
|
if (this.constructor.shadowRootInit) {
|
||||||
|
host = this.attachShadow(this.constructor.shadowRootInit);
|
||||||
|
}
|
||||||
const tree = this.template;
|
const tree = this.template;
|
||||||
|
|
||||||
// TODO: smart diffing
|
// TODO: smart diffing
|
||||||
if (JSON.stringify(this.#prevDOM) !== JSON.stringify(tree)) {
|
if (JSON.stringify(this.#prevDOM) !== JSON.stringify(tree)) {
|
||||||
const el = createElement(tree);
|
const el = createElement(tree);
|
||||||
if (el) {
|
if (el) {
|
||||||
if (Array.isArray(el)) this.replaceChildren(...el);
|
if (Array.isArray(el)) host.replaceChildren(...el);
|
||||||
else this.replaceChildren(el);
|
else host.replaceChildren(el);
|
||||||
}
|
}
|
||||||
this.#prevDOM = tree;
|
this.#prevDOM = tree;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue