element-vir
Version:
Heroic. Reactive. Declarative. Type safe. Web components without compromise.
90 lines (89 loc) • 3.13 kB
JavaScript
import { assert, assertWrap, check } from '@augment-vir/assert';
import { directive, Directive } from '../../lit-exports/all-lit-exports.js';
import { assertIsElementPartInfo } from './directive-helpers.js';
const directiveName = 'onIntersect';
/**
* A directive that fires its listener any time the element's configured "intersection" is crossed.
* This is commonly use for detecting when an element has scrolled into view. This uses the
* [built-in `IntersectionObserver`
* API](https://developer.mozilla.org/docs/Web/API/IntersectionObserver/IntersectionObserver), so it
* is very efficient.
*
* @category Directives
* @example
*
* ```ts
* import {html, defineElement, onIntersect} from 'element-vir';
*
* const MyElement = defineElement()({
* tagName: 'my-element',
* render() {
* return html`
* <div
* ${onIntersect({threshold: 1}, ({element, entry}) => {
* if (entry.isIntersecting) {
* console.log('is intersecting!');
* } else {
* console.log('is not intersecting');
* }
* })}
* >
* Some div
* </div>
* `;
* },
* });
* ```
*/
export const onIntersect = directive(class extends Directive {
element;
options;
intersectionObserver;
callback;
constructor(partInfo) {
super(partInfo);
assertIsElementPartInfo(partInfo, directiveName);
}
fireCallback(entries, observer) {
assert.isLengthAtLeast(entries, 1);
void this.callback?.({
element: assertWrap.isDefined(this.element),
allEntries: entries,
observer,
entry: entries[0],
});
}
update(partInfo, [options, callback,]) {
assertIsElementPartInfo(partInfo, directiveName);
this.callback = callback;
let needsObserving = false;
const newOptions = options;
const oldOptions = this.options;
if (!this.intersectionObserver ||
!oldOptions ||
!check.entriesEqual(newOptions, oldOptions)) {
this.options = options;
this.intersectionObserver?.disconnect();
this.intersectionObserver = new IntersectionObserver((entries, observer) => this.fireCallback(entries, observer), options);
needsObserving = true;
}
const newElement = partInfo.element;
const oldElement = this.element;
// if the element changes we need to observe the new one
if (newElement !== oldElement) {
this.element = newElement;
if (oldElement) {
this.intersectionObserver.unobserve(oldElement);
}
needsObserving = true;
}
if (needsObserving) {
this.intersectionObserver.observe(newElement);
}
return this.render(options, callback);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
render(options, callback) {
return undefined;
}
});