stencil-quantum
Version:
Experience the quantum realm of stencil.
132 lines (131 loc) • 4.63 kB
JavaScript
import { forceUpdate } from "@stencil/core";
import { log } from "./utils";
import { QuantumError } from "./error";
const $providers = Symbol.for("stencil-quantum-providers");
export class Provider {
constructor(key, value, debug = false) {
this.key = key;
this.value = value;
this.debug = debug;
this.listeners = [];
this.hooks = new Map();
this.mutable = false;
this.paused = false;
this.retrieve = () => {
return this.value;
};
this.provide = (value) => {
if (this.debug)
log(`(${String(this.key)}) `, "PROVIDING", value, "to", this.listeners);
let oldvalue = this.value;
this.value = value;
if (!this.paused) {
this.listeners
.filter(listener => !listener.paused)
.forEach(listener => listener.action(value, oldvalue));
}
return this;
};
this.update = (fn) => {
return this.provide(fn(this.retrieve()));
};
this.listen = (cb, updateImmediately = true, el) => {
if (this.debug)
log(`(${String(this.key)}) `, "LISTEN", updateImmediately, this, cb);
const listener = {
action: cb,
unlisten: () => this.unlisten(cb),
paused: false
};
this.listeners = [...this.listeners, listener];
if (updateImmediately)
cb(this.value);
return listener;
};
this.unlisten = (cb) => {
this.listeners = this.listeners.filter(listener => listener.action !== cb);
};
this.attach = (el, noHook) => {
if (this.debug)
log(`(${String(this.key)}) `, "Add Provider", el, this);
const providers = Provider.getAttached(el);
if (!providers.includes(this))
providers.push(this);
if (this.debug)
log(`(${String(this.key)}) `, "Total Providers", el, providers);
return noHook ? this : this.hook(el);
};
this.isHooked = (el) => {
return this.hooks.has(el);
};
this.getHook = (el) => this.hooks.get(el);
this.hook = (el) => {
if (this.debug)
log(`(${String(this.key)}) `, "Hook Provider", el, this);
this.hooks.set(el, this.listen(() => forceUpdate(el)));
return this;
};
this.pauseHook = (el, paused = true) => {
if (this.debug)
log(`(${String(this.key)}) `, "Pausing Hook", paused);
if (this.isHooked(el))
this.getHook(el).paused = paused;
};
this.unhook = (el) => {
var _a;
if (this.debug)
log(`(${String(this.key)}) `, "Unhook Provider", el, this);
if (this.isHooked(el)) {
(_a = this.hooks.get(el)) === null || _a === void 0 ? void 0 : _a.unlisten();
this.hooks.delete(el);
}
return this;
};
this.destroy = () => {
this.listeners = [];
this.hooks = new Map();
};
}
pause(paused = true) {
this.paused = paused;
}
/**
* Creates a predicate, that filters providers matching a key and having no namespace or being
* @param key
* @param namespace
*/
static makeFilter(key, namespace) {
return (p) => p.key === key || (namespace && typeof key === "string" && p.key === namespace + "__" + key);
}
static find(el, key, namespace, debug) {
var _a, _b, _c, _d;
if (debug)
log(`(${String(key)}) `, "Searching Provider", key, namespace, el);
const providers = Provider.getAttached(el).filter(Provider.makeFilter(key, namespace));
if (providers.length > 1) {
throw new QuantumError(`Found multiple "${String(key)}" providers on the same object!`);
}
else if (providers.length === 1) {
return providers[0];
}
// No provider found on this el, check parent
let parent = ((_c = (_a = el.parentElement) !== null && _a !== void 0 ? _a : (_b = el.shadowRoot) === null || _b === void 0 ? void 0 : _b.host) !== null && _c !== void 0 ? _c : (_d = el.parentNode) === null || _d === void 0 ? void 0 : _d.host); // parentElement or shadowRoot.host
if (!parent) {
throw new QuantumError(`No provider in hierarchy found that matches "${String(key)}"!`);
}
return Provider.find(parent, key, namespace, debug).attach(el, true); // Attach reference to this el to make lookup for children shorter
}
static create(el, key, value, namespace, debug) {
if (typeof key === "string")
key = (namespace ? namespace + "__" : "") + key;
if (debug)
log(`(${String(key)}) `, "Create Provider", el, key, value);
return new Provider(key, value, debug).attach(el);
}
static getAttached(el) {
if (!(el[$providers] instanceof Array)) {
el[$providers] = [];
}
return el[$providers];
}
}