Buckets:
| /** | |
| * @license | |
| * Copyright 2021 Google LLC | |
| * SPDX-License-Identifier: BSD-3-Clause | |
| */ | |
| import {ContextRequestEvent} from '../context-request-event.js'; | |
| import {ValueNotifier} from '../value-notifier.js'; | |
| import type {Context, ContextType} from '../create-context.js'; | |
| import type { | |
| ReactiveController, | |
| ReactiveControllerHost, | |
| } from '@lit/reactive-element'; | |
| declare global { | |
| interface HTMLElementEventMap { | |
| /** | |
| * A 'context-provider' event can be emitted by any element which hosts | |
| * a context provider to indicate it is available for use. | |
| */ | |
| 'context-provider': ContextProviderEvent<Context<unknown, unknown>>; | |
| } | |
| } | |
| export class ContextProviderEvent< | |
| C extends Context<unknown, unknown>, | |
| > extends Event { | |
| readonly context: C; | |
| readonly contextTarget: Element; | |
| /** | |
| * | |
| * @param context the context which this provider can provide | |
| * @param contextTarget the original context target of the provider | |
| */ | |
| constructor(context: C, contextTarget: Element) { | |
| super('context-provider', {bubbles: true, composed: true}); | |
| this.context = context; | |
| this.contextTarget = contextTarget; | |
| } | |
| } | |
| export interface Options<C extends Context<unknown, unknown>> { | |
| context: C; | |
| initialValue?: ContextType<C>; | |
| } | |
| type ReactiveElementHost = Partial<ReactiveControllerHost> & HTMLElement; | |
| /** | |
| * A ReactiveController which adds context provider behavior to a | |
| * custom element. | |
| * | |
| * This controller simply listens to the `context-request` event when | |
| * the host is connected to the DOM and registers the received callbacks | |
| * against its observable Context implementation. | |
| * | |
| * The controller may also be attached to any HTML element in which case it's | |
| * up to the user to call hostConnected() when attached to the DOM. This is | |
| * done automatically for any custom elements implementing | |
| * ReactiveControllerHost. | |
| */ | |
| export class ContextProvider< | |
| T extends Context<unknown, unknown>, | |
| HostElement extends ReactiveElementHost = ReactiveElementHost, | |
| > | |
| extends ValueNotifier<ContextType<T>> | |
| implements ReactiveController | |
| { | |
| protected readonly host: HostElement; | |
| private readonly context: T; | |
| constructor(host: HostElement, options: Options<T>); | |
| /** @deprecated Use new ContextProvider(host, options) */ | |
| constructor(host: HostElement, context: T, initialValue?: ContextType<T>); | |
| constructor( | |
| host: HostElement, | |
| contextOrOptions: T | Options<T>, | |
| initialValue?: ContextType<T> | |
| ) { | |
| super( | |
| (contextOrOptions as Options<T>).context !== undefined | |
| ? (contextOrOptions as Options<T>).initialValue | |
| : initialValue | |
| ); | |
| this.host = host; | |
| if ((contextOrOptions as Options<T>).context !== undefined) { | |
| this.context = (contextOrOptions as Options<T>).context; | |
| } else { | |
| this.context = contextOrOptions as T; | |
| } | |
| this.attachListeners(); | |
| this.host.addController?.(this); | |
| } | |
| onContextRequest = ( | |
| ev: ContextRequestEvent<Context<unknown, unknown>> | |
| ): void => { | |
| // Only call the callback if the context matches. | |
| if (ev.context !== this.context) { | |
| return; | |
| } | |
| // Also, in case an element is a consumer AND a provider | |
| // of the same context, we want to avoid the element to self-register. | |
| const consumerHost = ev.contextTarget ?? ev.composedPath()[0]; | |
| if (consumerHost === this.host) { | |
| return; | |
| } | |
| ev.stopPropagation(); | |
| this.addCallback(ev.callback, consumerHost, ev.subscribe); | |
| }; | |
| /** | |
| * When we get a provider request event, that means a child of this element | |
| * has just woken up. If it's a provider of our context, then we may need to | |
| * re-parent our subscriptions, because is a more specific provider than us | |
| * for its subtree. | |
| */ | |
| onProviderRequest = ( | |
| ev: ContextProviderEvent<Context<unknown, unknown>> | |
| ): void => { | |
| // Ignore events when the context doesn't match. | |
| if (ev.context !== this.context) { | |
| return; | |
| } | |
| // Also, in case an element is a consumer AND a provider | |
| // of the same context it shouldn't provide to itself. | |
| const childProviderHost = ev.contextTarget ?? ev.composedPath()[0]; | |
| if (childProviderHost === this.host) { | |
| return; | |
| } | |
| // Re-parent all of our subscriptions in case this new child provider | |
| // should take them over. | |
| const seen = new Set<unknown>(); | |
| for (const [callback, {consumerHost}] of this.subscriptions) { | |
| // Prevent infinite loops in the case where a one host element | |
| // is providing the same context multiple times. | |
| // | |
| // While normally it's a no-op to attempt to re-parent a subscription | |
| // that already has its proper parent, in the case where there's more | |
| // than one ValueProvider for the same context on the same hostElement, | |
| // they will each call the consumer, and since they will each have their | |
| // own dispose function, a well behaved consumer will notice the change | |
| // in dispose function and call their old one. | |
| // | |
| // This will cause the subscriptions to thrash, but worse, without this | |
| // set check here, we can end up in an infinite loop, as we add and remove | |
| // the same subscriptions onto the end of the map over and over. | |
| if (seen.has(callback)) { | |
| continue; | |
| } | |
| seen.add(callback); | |
| consumerHost.dispatchEvent( | |
| new ContextRequestEvent(this.context, consumerHost, callback, true) | |
| ); | |
| } | |
| ev.stopPropagation(); | |
| }; | |
| private attachListeners() { | |
| this.host.addEventListener('context-request', this.onContextRequest); | |
| this.host.addEventListener('context-provider', this.onProviderRequest); | |
| } | |
| hostConnected(): void { | |
| // emit an event to signal a provider is available for this context | |
| this.host.dispatchEvent(new ContextProviderEvent(this.context, this.host)); | |
| } | |
| } | |
Xet Storage Details
- Size:
- 5.83 kB
- Xet hash:
- 09ae63526319faf28f0befc7f9995a30926033c04f007a675bac28472b9b2aa1
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.