Last active
December 23, 2020 20:39
-
-
Save DeadWisdom/62bff0179366fff137038f0bdf616e11 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ConsumerEvent extends Event { | |
constructor(name) { | |
super('consume-' + name, { bubbles: true, cancelable: true }); | |
this.provider = null; | |
this.providerEvent = name + '-changed'; | |
} | |
} | |
const providers = new WeakMap(); | |
export function provide(source, name, value, providerEvent) { | |
providerEvent = providerEvent || name + '-changed'; | |
this.provide(source, name, value); | |
let providing = providers.get(source); | |
// Register our source as a provider | |
if (providing === undefined) { | |
providing = new Map(); | |
providers.set(source, providing); | |
} | |
if (!providing.has(name)) { | |
providing.set(name, value); | |
// Listen for a consume-*name* event, notify consumer that we promise to provide | |
source.addEventListener('consume-' + name, e => { | |
e.provider = source; | |
e.providerEvent = providerEvent; | |
e.value = providing[name]; | |
e.stopPropagation(); | |
e.preventDefault(); | |
}); | |
} else { | |
// Updated our stored value | |
providing[name] = value; | |
} | |
// Notify of value change | |
let evt = new CustomEvent(providerEvent, {detail: { value: value, provider: source }}); | |
source.dispatchEvent(evt); | |
} | |
export function consume(consumer, name, callback) { | |
// Send the consumer event | |
let evt = new ConsumerEvent(name) | |
consumer.dispatchEvent(evt); | |
if (!evt.provider) { | |
// We couldn't find a provider up the DOM, so we're done here. | |
return null; | |
} | |
// Since a provider was found, we send the callback an artificial initial update event: | |
let initialEvent = new CustomEvent(name, { detail: { value: evt.value, provider: evt.provider, initial: true } }); | |
callback(initialEvent); | |
// We could simply return the event to the consumer so they can handle | |
// registration themselves, but for this example, we'll subscribe to the event: | |
evt.provider.addEventListener(evt.providerEvent, callback); | |
// If we do that, we can further provide a disconnector for convenience: | |
evt.disconnect = () => evt.provider.removeEventListener(evt.providerEvent, callback); | |
// Return the event, the consumer gets to know who the provider is | |
return evt; | |
} | |
// Usage //// | |
// A provider simply calls this as many times as necessary, whenever the data updates: | |
// This will trigger 'user-changed' on the element. | |
class UserProvider extends LitElement { | |
onUserUpdate(userData) { | |
provide(this, 'user', userData); | |
} | |
} | |
// Or the provider could use anything as the source. Now the window itself is triggering 'user-changed': | |
provide(window, 'user', userData); | |
// A consumer simply consumes as needed: | |
class UserConsumer extends LitElement { | |
connectedCallback() { | |
super.connectedCallback(); | |
this._userChangeSubscription = consume(this, 'user', this._onUserChange = this.onUserChange.bind(this)); | |
} | |
disconnectedCallback() { | |
super.disconnectedCallback(); | |
if (this._userChangeSubscription) { | |
this._userChangeSubscription.disconnect(); | |
} | |
} | |
onUserChange(e) { | |
this.user = e.detail.value; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment