feat: new attachEffect utility
This commit is contained in:
parent
aa30c99487
commit
a9b9ada5e3
3 changed files with 53 additions and 20 deletions
|
@ -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>`;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
*/
|
||||||
|
|
|
@ -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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue