initial attach effect

This commit is contained in:
Ayo 2023-11-26 02:26:30 +01:00
parent 0209095d0a
commit d2a96b4fa4
6 changed files with 76 additions and 41 deletions

17
attach-effect/Counter.mjs Normal file
View file

@ -0,0 +1,17 @@
// @ts-check
import WebComponent from "../src/WebComponent.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);
}
get template() {
return `<button id="btn">${this.props.count}</button>`;
}
}
customElements.define("my-counter", Counter);

13
attach-effect/index.html Normal file
View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>WC demo</title>
<script type="module" src="Counter.mjs"></script>
</head>
<body>
<h1>Attach Effect Test</h1>
<my-counter></my-counter>
</body>
</html>

View file

@ -1,2 +0,0 @@
shamefully-hoist=true
strict-peer-dependencies=false

View file

@ -1,2 +1 @@
import McFly from "@mcflyjs/config";
export default defineNitroConfig({ ...McFly() });
export default defineNitroConfig({ extends: '@mcflyjs/config' });

View file

@ -87,72 +87,74 @@ export class WebComponent extends HTMLElement {
this.onDestroy();
}
/**
* @param {string} property
* @param {any} previousValue
* @param {any} currentValue
*/
attributeChangedCallback(property, previousValue, currentValue) {
attributeChangedCallback(property, previousValue, currentValue) {
const camelCaps = this.#getCamelCaps(property);
if (previousValue !== currentValue) {
this[property] = currentValue === "" || currentValue;
this[camelCaps] = this[property]; // remove on v2
this.props[camelCaps] = this[property];
this.props[camelCaps] = {
attributeChanged: true,
value: this[property],
}
this.render();
this.onChanges({ property, previousValue, currentValue });
}
}
/**
* Converts a kebab-cased string into camelCaps
* @param {string} kebab string in kebab-case
* @returns {string}
*/
#getCamelCaps(kebab) {
return kebab.replace(/-./g, (x) => x[1].toUpperCase());
}
/**
* Proxy handler for observed attribute - property counterpart
* @param {(qualifiedName: string, value: string) => void} setter
* @returns
*/
#handler = (setter) => ({
set(obj, prop, newValue) {
#typeMap = {}
#handler = (setter, typeMap) => ({
set(obj, prop, value) {
const attributeChanged = value?.attributeChanged ?? false;
let newValue = attributeChanged ? value.value : value;
const oldValue = obj[prop];
obj[prop] = newValue;
console.log(">>>", newValue, oldValue);
/**
* Converts camelCaps string into kebab-case
* @param {string} str
* @returns {string}
*/
const getKebab = (str) =>
str.replace(
/[A-Z]+(?![a-z])|[A-Z]/g,
($, ofs) => (ofs ? "-" : "") + $.toLowerCase()
);
if (!(prop in typeMap)) {
typeMap[prop] = typeof newValue;
}
if (oldValue != newValue) {
obj[prop] = restoreType(newValue, typeMap[prop])
if (attributeChanged && !oldValue != newValue) {
const kebab = getKebab(prop);
setter(kebab, newValue);
}
function getKebab(str) {
return str.replace(
/[A-Z]+(?![a-z])|[A-Z]/g,
($, ofs) => (ofs ? "-" : "") + $.toLowerCase()
);
}
function restoreType(value, type) {
switch(type) {
case 'string': return value;
case 'number': return parseInt(value);
case 'boolean': return JSON.parse(value);
default: return value
}
}
return true;
},
});
/**
* Initialize the `props` proxy object
*/
#initializeProps() {
if (!this.#props) {
this.#props = new Proxy(
{},
this.#handler((key, value) => this.setAttribute(key, value))
this.#handler((key, value) => this.setAttribute(key, value), this.#typeMap)
);
}
}

6
src/attach-effect.js Normal file
View file

@ -0,0 +1,6 @@
export function attachEffect(proxy, prop, callback) {
proxy[prop] = {
attach: 'effect',
callback
}
}