blob: b37a978c12759c874f71bdeb2464cf67814be718 [file] [log] [blame]
/**
* @license
* Copyright 2021 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {ReactiveController, ReactiveControllerHost} from 'lit';
import {Observable, Subscription} from 'rxjs';
const SUBSCRIPTION_SYMBOL = Symbol('subscriptions');
// Checks whether a subscription can be added. Returns true if it can be added,
// return false if it's already present.
// Subscriptions are stored on the host so they have the same life-time as the
// host.
function checkSubscription<T>(
host: ReactiveControllerHost,
obs$: Observable<T>,
setProp: (t: T) => void
): boolean {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const hostSubscriptions = ((host as any)[SUBSCRIPTION_SYMBOL] ||= new Map());
if (!hostSubscriptions.has(obs$)) hostSubscriptions.set(obs$, new Set());
const obsSubscriptions = hostSubscriptions.get(obs$);
if (obsSubscriptions.has(setProp)) return false;
obsSubscriptions.add(setProp);
return true;
}
/**
* Enables components to simply hook up a property with an Observable like so:
*
* subscribe(this, obs$, x => (this.prop = x));
*/
export function subscribe<T>(
host: ReactiveControllerHost,
obs$: Observable<T>,
setProp: (t: T) => void
) {
if (!checkSubscription(host, obs$, setProp)) return;
host.addController(new SubscriptionController(obs$, setProp));
}
export class SubscriptionController<T> implements ReactiveController {
private sub?: Subscription;
constructor(
private readonly obs$: Observable<T>,
private readonly setProp: (t: T) => void
) {}
hostConnected() {
this.sub = this.obs$.subscribe(this.setProp);
}
hostDisconnected() {
this.sub?.unsubscribe();
}
}