@pionjs/pion
Version:
Hooks for web components
61 lines (60 loc) • 2.1 kB
JavaScript
import { hook, Hook } from "./hook";
const UPPER = /([A-Z])/gu;
export const useProperty = hook(class extends Hook {
property;
eventName;
constructor(id, state, property, initialValue) {
super(id, state);
if (this.state.virtual) {
throw new Error("Can't be used with virtual components.");
}
this.updater = this.updater.bind(this);
this.property = property;
this.eventName =
property.replace(UPPER, "-$1").toLowerCase() + "-changed";
// set the initial value only if it was not already set by the parent
if (this.state.host[this.property] != null)
return;
if (typeof initialValue === "function") {
const initFn = initialValue;
initialValue = initFn();
}
if (initialValue == null)
return;
this.updater(initialValue, true);
}
update(ignored, ignored2) {
return [this.state.host[this.property], this.updater];
}
resolve(valueOrUpdater) {
const previousValue = this.state.host[this.property];
const updater = typeof valueOrUpdater === "function"
? valueOrUpdater
: undefined;
const value = updater
? updater(previousValue)
: valueOrUpdater;
return [previousValue, value, updater];
}
notify(value, updater) {
const ev = new CustomEvent(this.eventName, {
detail: { value, updater, path: this.property },
cancelable: true,
});
this.state.host.dispatchEvent(ev);
return ev;
}
updater(valueOrUpdater, isInit = false) {
const [previousValue, value, updater] = this.resolve(valueOrUpdater);
const ev = this.notify(value, updater);
if (!isInit && ev.defaultPrevented)
return;
if (Object.is(previousValue, value))
return;
this.state.host[this.property] = value;
}
});
export const lift = (setter) => (ev) => {
ev.preventDefault();
setter(ev.detail.updater ?? ev.detail.value);
};