rdesign/frontend/node_modules/@lit/context/development/lib/context-root.js

94 lines
3.9 KiB
JavaScript

/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: BSD-3-Clause
*/
import { ContextRequestEvent } from './context-request-event.js';
/**
* A ContextRoot can be used to gather unsatisfied context requests and
* re-dispatch them when new providers which satisfy matching context keys are
* available.
*
* This allows providers to be added to a DOM tree, or upgraded, after the
* consumers.
*/
export class ContextRoot {
constructor() {
this.pendingContextRequests = new Map();
this.onContextProvider = (event) => {
const pendingRequestData = this.pendingContextRequests.get(event.context);
if (pendingRequestData === undefined) {
// No pending requests for this context at this time
return;
}
// Clear our list. Any still unsatisfied requests will re-add themselves
// when we dispatch the events below.
this.pendingContextRequests.delete(event.context);
// Loop over all pending requests and re-dispatch them from their source
const { requests } = pendingRequestData;
for (const { elementRef, callbackRef } of requests) {
const element = elementRef.deref();
const callback = callbackRef.deref();
if (element === undefined || callback === undefined) {
// The element was GC'ed. Do nothing.
}
else {
// Re-dispatch if we still have the element and callback
element.dispatchEvent(new ContextRequestEvent(event.context, element, callback, true));
}
}
};
this.onContextRequest = (event) => {
// Events that are not subscribing should not be buffered
if (event.subscribe !== true) {
return;
}
// Note, it's important to use the initial target
// since that's the requesting element and the event may be re-targeted
// to an outer host element.
const element = (event.contextTarget ??
event.composedPath()[0]);
const callback = event.callback;
let pendingContextRequests = this.pendingContextRequests.get(event.context);
if (pendingContextRequests === undefined) {
this.pendingContextRequests.set(event.context, (pendingContextRequests = {
callbacks: new WeakMap(),
requests: [],
}));
}
let callbacks = pendingContextRequests.callbacks.get(element);
if (callbacks === undefined) {
pendingContextRequests.callbacks.set(element, (callbacks = new WeakSet()));
}
if (callbacks.has(callback)) {
// We're already tracking this element/callback pair
return;
}
callbacks.add(callback);
pendingContextRequests.requests.push({
elementRef: new WeakRef(element),
callbackRef: new WeakRef(callback),
});
};
}
/**
* Attach the ContextRoot to a given element to intercept `context-request` and
* `context-provider` events.
*
* @param element an element to add event listeners to
*/
attach(element) {
element.addEventListener('context-request', this.onContextRequest);
element.addEventListener('context-provider', this.onContextProvider);
}
/**
* Removes the ContextRoot event listeners from a given element.
*
* @param element an element from which to remove event listeners
*/
detach(element) {
element.removeEventListener('context-request', this.onContextRequest);
element.removeEventListener('context-provider', this.onContextProvider);
}
}
//# sourceMappingURL=context-root.js.map