UNPKG

stencil-quantum

Version:

Experience the quantum realm of stencil.

132 lines (131 loc) 4.63 kB
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]; } }