chore: update site, readme, & examples for v2.0 features

This commit is contained in:
Ayo 2023-12-10 20:17:46 +01:00
parent 4d4fdb3d89
commit 4c885ccf9d
7 changed files with 209 additions and 4338 deletions

110
README.md
View file

@ -5,46 +5,53 @@
[![Package information: NPM downloads](https://img.shields.io/npm/dt/web-component-base)](https://www.npmjs.com/package/web-component-base)
[![Bundle Size](https://img.shields.io/bundlephobia/minzip/web-component-base)](https://bundlephobia.com/package/web-component-base)
> A zero-dependency, ~600 Bytes (minified & gzipped), JS base class for creating reactive custom elements easily
🤷‍♂️ zero-dependency, 🤏 tiny JS base class for creating reactive custom elements easily
When you extend the `WebComponent` class for your component, you only have to define the `template` and `properties`. Any change in any property value will automatically cause just the component UI to render.
The result is a reactive UI on property changes. [View on CodePen ↗](https://codepen.io/ayoayco-the-styleful/pen/ZEwoNOz?editors=1010)
<details>
<summary>Table of Contents</summary>
<ol>
<li><a href="#import-via-unpkg">Import via unpkg</a></li>
<li><a href="#installation-via-npm">Installation via npm</a></li>
<li><a href="#usage">Usage</a></li>
<li><a href="#template-vs-render">`template` vs `render()`</a></li>
<li><a href="#prop-access">Prop access</a>
<ol>
<li><a href="#alternatives">Alternatives</a></li>
</ol>
</li>
<li><a href="#quick-start-example">Quick Start Example</a></li>
<li><a href="#life-cycle-hooks">Life-Cycle Hooks</a>
<ol>
<li><a href="#oninit">`onInit`</a> - the component is connected to the DOM, before view is initialized</li>
<li><a href="#afterviewinit">`afterViewInit`</a> - after the view is first initialized</li>
<li><a href="#ondestroy">`onDestroy`</a> - the component is disconnected from the DOM</li>
<li><a href="#onchanges">`onChanges`</a> - every time an attribute value changes</li>
</ol>
</li>
<li><a href="#library-size">Library Size</a></li>
</ol>
</details>
## Features
## Import via unpkg
- A props API that synchronizes your components' property values and HTML attributes
- Sensible life-cycle hooks that you understand and remember
- An html tagged templates powered by preact's html/mini
- Attach functions as "side effects" that gets triggered on property value changes with attachEffect (example)
- Provided out-of-the-box with McFly, a powerful no-framework framework
## Table of Contents
1. [Installation](#installation)
1. [Import via unpkg](#import-via-unpkg)
2. [Installation via npm](#installation-via-npm)
3. [Usage](#usage)
4. [`template` vs `render()`](#template-vs-render)
5. [Prop access](#prop-access)
1. [Alternatives](#alternatives)
6. [Quick Start Example](#quick-start-example)
7. [Life-Cycle Hooks](#life-cycle-hooks)
1. [`onInit`](#oninit) - the component is connected to the DOM, before view is initialized
2. [`afterViewInit`](#afterviewinit) - after the view is first initialized
3. [`onDestroy`](#ondestroy) - the component is disconnected from the DOM
4. [`onChanges`](#onchanges) - every time an attribute value changes
8. [Library Size](#library-size)
## Installation
The library is distributed as complete ESM modules, published on [NPM](https://ayco.io/n/web-component-base). Please file an issue in our [issue tracker](https://ayco.io/gh/web-component-base/issues) for problems or requests regarding our distribution.
### Import via unpkg (no bundlers needed!)
Import using [unpkg](https://unpkg.com/web-component-base) in your vanilla JS component. We will use this in the rest of our [usage examples](#usage).
Please check
```js
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
```
## Installation via npm
Usable for projects with bundlers or using import maps.
### Installation via npm
Usable for projects with bundlers or using import maps pointing to to the specific files in downloaded in your `node_modules/web-component-base`.
```bash
npm i web-component-base
@ -56,13 +63,17 @@ In your component class:
```js
// HelloWorld.mjs
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
class HelloWorld extends WebComponent {
static properties = ["my-name", "emotion"];
static props ={
myName: 'World',
emotion: 'sad'
}
get template() {
return `
<h1>Hello ${this.props.myName}${this.props.emotion === "sad" ? ". 😭" : "! 🙌"}</h1>`;
<h1>Hello ${this.props.myName}${this.props.emotion === "sad" ? ". 😭" : "! 🙌"}</h1>
`;
}
}
@ -108,13 +119,11 @@ This mental model attempts to reduce the cognitive complexity of authoring compo
```js
class HelloWorld extends WebComponent {
static properties = ["my-prop"];
onInit() {
let count = 0;
this.onclick = () => this.props.myProp = `${++count}`
static props = {
myProp: 'World'
}
get template() {
return `
return html`
<h1>Hello ${this.props.myProp}</h1>
`;
}
@ -133,7 +142,7 @@ this.props.myName = 'hello'
this.setAttribute('my-name','hello');
```
Therefore, this will tell the browser that the UI needs a render if the attribute is one of the component's observed attributes we explicitly provided with `static properties = ['my-name']`;
Therefore, this will tell the browser that the UI needs a render if the attribute is one of the component's observed attributes we explicitly provided with `static props`;
> [!NOTE]
> The `props` property of `WebComponent` works like `HTMLElement.dataset`, except `dataset` is only for attributes prefixed with `data-`. A camelCase counterpart using `props` will give read/write access to any attribute, with or without the `data-` prefix.
@ -155,10 +164,12 @@ Here is an example of using a custom element in a single .html file.
<head>
<title>WC Base Test</title>
<script type="module">
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
class HelloWorld extends WebComponent {
static properties = ["my-name"];
static props = {
myName: 'World'
}
get template() {
return `<h1>Hello ${this.props.myName}!</h1>`;
}
@ -188,7 +199,7 @@ Define behavior when certain events in the component's life cycle is triggered b
- Best for setting up the component
```js
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
class ClickableText extends WebComponent {
// gets called when the component is used in an HTML document
@ -225,7 +236,7 @@ class ClickableText extends WebComponent {
- best for undoing any setup done in `onInit()`
```js
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
class ClickableText extends WebComponent {
@ -252,7 +263,7 @@ class ClickableText extends WebComponent {
- Triggered when an attribute value changed
```js
import { WebComponent } from "https://unpkg.com/web-component-base@latest/WebComponent.min.js";
import { WebComponent } from "https://unpkg.com/web-component-base@latest/index.js";
class ClickableText extends WebComponent {
// gets called when an attribute value changes
@ -269,4 +280,15 @@ class ClickableText extends WebComponent {
## Library Size
The bundle size was reported to be 587 Bytes (minified & gzipped) by [bundlephobia](https://bundlephobia.com/package/web-component-base). Running [size-limit](https://npmjs.com/package/@size-limit/preset-small-lib) reports the base class size as around 760 Bytes (minified & brotlied), and using the `WebComponent.min.js` version gets it down to around 400 Bytes.
All the functions and the base class in the library are minimalist by design and only contains what is needed for their purpose.
As of the major release v2.0.0, the base class is around 1.7 kB (min + gzip) according to [bundlephobia.com](https://bundlephobia.com/package/web-component-base@2.0.0) and 1.1 kB (min + brotli) according to [size-limit](http://github.com/ai/size-limit). There is an increase in size before this release -- primarily for advanced features in building complex applications.
> [!NOTE]
> As a builder of both simple sites and complex web apps, I recognize that not all custom elements need advanced features for reactivity.
>
>I also don't want to get things in my code that I don't need (YAGNI -- You Aren't Gonna Need It)... and I want a base class for simpler use cases that don't have Proxy props or attaching effects...
>
>To address this, I am working on a "lite" version my base class... please stay tuned.
>
> -Ayo

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
@ -10,23 +10,19 @@
}
</style>
<script type="module">
import { WebComponent } from "https://unpkg.com/web-component-base@1.13.3/WebComponent.min.js";
import {
WebComponent,
html,
} from "https://unpkg.com/web-component-base@latest/index.js";
/**
* @see https://ayco.io/n/web-component-base
*/
class Counter extends WebComponent {
static properties = ["count"];
onInit() {
this.props.count = 0;
this.onclick = () => ++this.props.count;
}
onChanges(changes) {
console.log(changes);
// now click the button & check your devtools console
}
export class Counter extends WebComponent {
static props = {
count: 0,
};
get template() {
return `<button>${this.props.count}</button>`;
return html`<button onClick=${() => ++this.props.count}>
${this.props.count}
</button>`;
}
}

View file

@ -1,27 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>WC Base Test</title>
<script type="module">
import { WebComponent } from "./src/WebComponent.js";
class HelloWorld extends WebComponent {
static properties = ["my-name"];
get template() {
return `<h1>Hello ${this.props.myName}!</h1>`;
}
}
customElements.define("hello-world", HelloWorld);
</script>
</head>
<body>
<hello-world my-name="Ayo"></hello-world>
<script>
const helloWorld = document.querySelector("hello-world");
setTimeout(() => {
helloWorld.props.myName = "Ayo zzzZzzz";
}, 2500);
</script>
</body>
</html>

4188
site/package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
<style>
.my-header {
border-radius: 5px;
background: linear-gradient(45deg, #3054bf, #416fff);
background: linear-gradient(45deg, var(--color-primary), var(--color-fade));
color: white;
margin: 1em 0;
padding: 1.5em;

View file

@ -1,44 +1,47 @@
<div class="call-to-action">
<a id="primary"
href="https://ayco.io/gh/web-component-base#readme">
<a id="primary" href="https://ayco.io/gh/web-component-base#readme">
Learn More
</a>
<a href="https://codepen.io/ayoayco-the-styleful/pen/PoVegBK?editors=1010">Playground &rarr;</a>
<a
href="https://codepen.io/ayoayco-the-styleful/pen/PoVegBK?editors=1010"
target="_blank"
>Playground &rarr;</a
>
</div>
<style>
.call-to-action {
display: flex;
gap: 1em;
justify-content: center;
margin: 1em 0;
width: 100%;
.call-to-action {
display: flex;
gap: 1em;
justify-content: center;
margin: 1em 0;
width: 100%;
& a {
border: 3px solid var(--color-fade);
padding: 0.5em 0.75em;
border-radius: 5px;
text-align: center;
text-decoration: none;
& a {
border: 3px solid var(--color-fade);
padding: 0.5em 0.75em;
border-radius: 5px;
text-align: center;
text-decoration: none;
&:hover {
box-shadow: 0 0 0 3px var(--color-fade);
}
&:hover {
box-shadow: 0 0 0 3px var(--color-fade);
}
&#primary {
&#primary {
background: #3054bf;
color: white;
min-width: 35%;
}
}
}
}
@media only screen and (max-device-width: 430px) {
.call-to-action {
flex-direction: column;
& a {
width: 100% !important;
@media only screen and (max-device-width: 430px) {
.call-to-action {
flex-direction: column;
& a {
width: 100% !important;
}
}
}
}
</style>

View file

@ -1,4 +1,4 @@
<!DOCTYPE html>
<!doctype html>
<html lang="en">
<!--
Hello! This page is an example McFly page.
@ -18,6 +18,32 @@
url: "https://ayco.io",
};
</script>
<style>
@counter-style publish-icons {
system: cyclic;
symbols: "️✅";
suffix: " ";
}
main {
font-size: large;
& section ul {
list-style: publish-icons;
}
& hr {
margin: 2em 0;
}
& section p:not(blockquote *):first-of-type::first-letter {
font-weight: 900;
font-size: 2rem;
line-height: 2rem;
float: left;
color: var(--color-primary);
margin-top: -8px;
}
}
</style>
</my-head>
<body>
<awesome-header>
@ -25,61 +51,100 @@
<span slot="description">{{ project.description }}</span>
</awesome-header>
<main>
<section style="font-size: large;">
<section>
<p>
By extending our base class, you get an easy authoring experience as you would expect in writing your components:</p>
By extending our base class, you get the easy authoring experience you
have come to expect when coming from more mature JS frameworks-- all
with zero-dependency, minimal tooling and very thin abstraction over
the vanilla web platform.
</p>
<ul>
<li>A <code-block inline>props</code-block> API that synchronizes your components' property values and HTML attributes</li>
<li>
A robust <code-block inline>props</code-block> API that synchronizes
your components' property values and HTML attributes
</li>
<li>Sensible life-cycle hooks that you understand and remember</li>
<li>Extensible templates & renderer (examples in-progress)</li>
<li>Provided out-of-the-box with <a href="https://ayco.io/gh/McFly">McFly</a>, a powerful no-framework framework</li>
<li>
A minimal <code-block inline>html</code-block> function for tagged
templates powered by Preact's tiny (450 Bytes)
<a href="http://github.com/developit/htm" target="_blank"
>html/mini</a
>
</li>
<li>
Attach "side effects" that gets triggered on property value changes
with <code-block inline>attachEffect</code-block> (<a
href="https://codepen.io/ayoayco-the-styleful/pen/ExrdWPv?editors=1011"
target="_blank"
>example</a
>)
</li>
<li>
Provided out-of-the-box with
<a href="https://ayco.io/gh/McFly">McFly</a>, a powerful
no-framework framework
</li>
</ul>
<call-to-action></call-to-action>
<warning-block>See this issue for FAQs or comment your own: <a href="https://github.com/ayoayco/web-component-base/issues/3">Issue #3</a></warning-block>
<my-quote type="info">
<p>
Version 2.0.0-beta is out! 🎉
</p>
<p>
We are now able to attach "side effects" to property value changes, from inside the component and outside.
</p>
<p>
Play on <a href="https://codepen.io/ayoayco-the-styleful/pen/ExrdWPv?editors=1011">CodePen</a>.
</p>
</my-quote>
</section>
<hr style="margin: 2em 0;" />
<section style="font-size: large;">
<hr />
<section>
<p>Why use this base class?</p>
<p>
When you extend the WebComponent class for your component, you only have to define the template and properties. Any change in any property value will automatically cause just the component UI to render.
Writing Web Components from the vanilla
<code-block inline>HTMLElement</code-block> can easily get confusing
and hard to maintain, especially when coming from JS frameworks with
advanced tooling.
</p>
<p>
This project aims to ease this with the thinnest possible abstraction
from vanilla and virtually zero need for advanced tooling. It works
without bundlers, transpilers, or polyfills.
</p>
<p>
When you extend the <code-block inline>WebComponent</code-block> class
for your component, you only have to define the template and
properties. Any change in any property value will automatically cause
just the component UI to render.
</p>
<p>The result is a reactive UI on property changes: <my-counter></my-counter></p>
<p>
The result is a reactive UI on property changes:
<my-counter></my-counter>
</p>
<code-block language="js">
<pre>
import { WebComponent } from &quot;https://unpkg.com/web-component-base@1.13.3/WebComponent.min.js&quot;;
import { WebComponent, html } from &quot;https://unpkg.com/web-component-base@2.0.0/index.js&quot;;
export class Counter extends WebComponent {
static properties = [&quot;count&quot;];
onInit() {
this.props.count = 0;
this.onclick = () => ++this.props.count;
static props = {
count: 0
}
get template() {
return `&lt;button&gt;${this.props.count}&lt;/button&gt;`;
return html`
&lt;button onClick=${() => ++this.props.count}&gt;
${this.props.count}
&lt;/button&gt;
`;
}
}
customElements.define(&quot;my-counter&quot;, Counter);
</pre>
customElements.define(&quot;my-counter&quot;, Counter);</pre
>
</code-block>
</section>
</main>
<my-footer>
<span>This site is built with <a href="https://github.com/ayoayco/McFly">McFly</a>, the no-framework framework;
</span><br />
<span>✨ Star on <a href="https://github.com/ayoayco/web-component-base">GitHub</a> to support;
</span><br />
<span
>This site is built with
<a href="https://github.com/ayoayco/McFly">McFly</a>, the no-framework
framework; </span
><br />
<span
>✨ Star on
<a href="https://github.com/ayoayco/web-component-base">GitHub</a> to
support; </span
><br />
<span>
A project by <a href="{{ author.url }}">{{ author.name }}</a>
</span>