<cool-counter> with Signals
by WebComponents
HTML
<script>
// tiny Signals: swap for @preact/signals-core or the TC39 signal-polyfill without changing the component
let current = null; // the effect that is running now
const signal = (value) => {
const subs = new Set();
return {
get value() { current && subs.add(current); return value },
set value(v) { if (v !== value) { value = v; [...subs].forEach(fn => fn()) } }
};
};
const effect = (fn) => { const run = () => { current = run; fn(); current = null }; run(); return run };
const computed = (fn) => { const s = signal(); effect(() => s.value = fn()); return { get value() { return s.value } } };
customElements.define('cool-counter', class extends HTMLElement {
#count = signal(666);
#bg = computed(() => this.#count.value % 2 === 0 ? 'lightblue' : 'lightgreen');
get value() { return this.#count.value }
set value(v) { this.#count.value = v }
connectedCallback() {
const createElement = (tag, props = {}) => Object.assign(document.createElement(tag), props);
this.append(
createElement("style", {
textContent: "button{border-radius:4px;cursor:pointer;padding:0.5em 1em}"
}),
this.button = createElement("button", {
onclick: (evt) => this.value++
})
); // append
effect(() => {
this.button.textContent = `Increment! (count: ${this.value})`;
this.button.style.background = this.#bg.value;
});
} // connectedCallback
}) // define <cool-counter>
</script>
<cool-counter></cool-counter>