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 // @ts-check
import WebComponent from "../src/WebComponent.js"; 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 { export class Counter extends WebComponent {
static properties = ["count"]; static properties = ["count"];
onInit() { onInit() {
this.props.count = 0; this.props.count = 0;
this.onclick = () => ++this.props.count; 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() { get template() {
return `<button id="btn">${this.props.count}</button>`; 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. * 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
* @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset
* @typedef {{[name: string]: any}} PropStringMap
* @type {PropStringMap} * @type {PropStringMap}
*/ */
get props() { get props() {
@ -53,11 +52,7 @@ export class WebComponent extends HTMLElement {
/** /**
* Triggered when an attribute value changes * Triggered when an attribute value changes
* @param {{ * @param {Changes} changes
* property: string,
* previousValue: any,
* currentValue: any
* }} changes
*/ */
onChanges(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) => { const getKebab = (str) => {
return str.replace( return str.replace(
/[A-Z]+(?![a-z])|[A-Z]/g, /[A-Z]+(?![a-z])|[A-Z]/g,
@ -141,14 +140,25 @@ export class WebComponent extends HTMLElement {
typeMap[prop] = typeof value; 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; obj[prop] = value;
effectsMap[prop]?.forEach((f) => f(value));
const kebab = getKebab(prop); const kebab = getKebab(prop);
setter(kebab, value); setter(kebab, value);
} }
return true; 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) { if (!this.#props) {
this.#props = new Proxy( this.#props = new Proxy(
{}, {},
this.#handler( this.#handler((key, value) => this.setAttribute(key, value), this)
(key, value) => this.setAttribute(key, value),
this.#typeMap
)
); );
} }
} }
} }
export default WebComponent; 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', * @typedef { import('./WebComponent.js').Changes} Changes
callback * @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,
};
} }