feat: new attachEffect utility

This commit is contained in:
Ayo 2023-11-30 20:04:41 +01:00
parent aa30c99487
commit a9b9ada5e3
3 changed files with 53 additions and 20 deletions

View file

@ -1,14 +1,22 @@
// @ts-check
import WebComponent from "../src/WebComponent.js";
// import { attachEffect } from "../src/attach-effect.js";
import { attachEffect } from "../src/attach-effect.js";
export class Counter extends WebComponent {
static properties = ["count"];
onInit() {
this.props.count = 0;
this.onclick = () => ++this.props.count;
// attachEffect(this.props, 'count', console.log);
attachEffect(
this.props.count,
(count) => console.log(count)
);
}
afterViewInit(){
attachEffect(this.props.count, (count) => console.log(count + 100));
}
get template() {
return `<button id="btn">${this.props.count}</button>`;
}

View file

@ -24,7 +24,6 @@ export class WebComponent extends HTMLElement {
* Read-only property containing camelCase counterparts of observed attributes.
* @see https://www.npmjs.com/package/web-component-base#prop-access
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
* @typedef {{[name: string]: any}} PropStringMap
* @type {PropStringMap}
*/
get props() {
@ -53,11 +52,7 @@ export class WebComponent extends HTMLElement {
/**
* Triggered when an attribute value changes
* @param {{
* property: string,
* previousValue: any,
* currentValue: any
* }} changes
* @param {Changes} changes
*/
onChanges(changes) {}
@ -125,7 +120,11 @@ export class WebComponent extends HTMLElement {
}
};
#handler(setter, typeMap) {
#effectsMap = {};
#handler(setter, meta) {
const effectsMap = meta.#effectsMap;
const typeMap = meta.#typeMap;
const getKebab = (str) => {
return str.replace(
/[A-Z]+(?![a-z])|[A-Z]/g,
@ -141,14 +140,25 @@ export class WebComponent extends HTMLElement {
typeMap[prop] = typeof value;
}
if (oldValue !== value) {
if (value.attach === "effect") {
if (!effectsMap[prop]) {
effectsMap[prop] = [];
}
effectsMap[prop].push(value.callback);
} else if (oldValue !== value) {
obj[prop] = value;
effectsMap[prop]?.forEach((f) => f(value));
const kebab = getKebab(prop);
setter(kebab, value);
}
return true;
},
get(obj, prop) {
Object.getPrototypeOf(obj[prop]).proxy = meta.#props;
Object.getPrototypeOf(obj[prop]).prop = prop;
return obj[prop];
},
};
}
@ -156,13 +166,19 @@ export class WebComponent extends HTMLElement {
if (!this.#props) {
this.#props = new Proxy(
{},
this.#handler(
(key, value) => this.setAttribute(key, value),
this.#typeMap
)
this.#handler((key, value) => this.setAttribute(key, value), this)
);
}
}
}
export default WebComponent;
/**
* @typedef {{
* property: string,
* previousValue: any,
* currentValue: any
* }} Changes
* @typedef {{[name: string]: any}} PropStringMap
*/

View file

@ -1,6 +1,15 @@
export function attachEffect(proxy, prop, callback) {
proxy[prop] = {
attach: 'effect',
callback
}
}
/**
*
* @typedef { import('./WebComponent.js').Changes} Changes
* @typedef { import('./WebComponent.js').PropStringMap} PropStringMap
* @param {Object} obj
* @param {(newValue: any) => void} callback
*/
export function attachEffect(obj, callback) {
const { proxy, prop } = Object.getPrototypeOf(obj);
proxy[prop] = {
attach: "effect",
callback,
};
}