UNPKG

element-vir

Version:

Heroic. Reactive. Declarative. Type safe. Web components without compromise.

90 lines (89 loc) 3.13 kB
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; } });