UNPKG

sanity

Version:

Sanity is a real-time content infrastructure with a scalable, hosted backend featuring a Graph Oriented Query Language (GROQ), asset pipelines and fast edge caches

111 lines (97 loc) 3.48 kB
import {merge, Observable, of as observableOf, Subject} from 'rxjs' import {filter, map, mergeMap} from 'rxjs/operators' import {orientationChange$} from './orientationChange' import {resize$} from './resize' import {scroll$} from './scroll' const ROOT_MARGIN_PX = 150 /* Adapted from the polyfill at https://github.com/WICG/IntersectionObserver */ function isIntersectionObserverSupported() { if ( typeof window !== 'undefined' && 'IntersectionObserver' in window && 'IntersectionObserverEntry' in window && 'intersectionRatio' in IntersectionObserverEntry.prototype ) { // Minimal polyfill for Edge 15's lack of `isIntersecting` // See: https://github.com/WICG/IntersectionObserver/issues/211 if (!('isIntersecting' in IntersectionObserverEntry.prototype)) { Object.defineProperty(IntersectionObserverEntry.prototype, 'isIntersecting', { get() { return this.intersectionRatio > 0 }, }) } return true } return false } type IntersectionEvent = {isIntersecting: boolean} export const intersectionObservableFor = isIntersectionObserverSupported() ? createIntersectionObserverBased() : createLegacyBased() type IntersectionObservableFor = (element: Element) => Observable<IntersectionEvent> function createIntersectionObserverBased(): IntersectionObservableFor { const intersectionObserverEntriesSubject = new Subject<IntersectionObserverEntry>() const intersectionObserver = new IntersectionObserver( (entries) => { entries.forEach((entry) => { intersectionObserverEntriesSubject.next(entry) }) }, { threshold: 0, rootMargin: `${ROOT_MARGIN_PX}px`, }, ) // eslint-disable-next-line @typescript-eslint/no-shadow return function intersectionObservableFor(element) { return new Observable((observer) => { intersectionObserver.observe(element) observer.next() return () => intersectionObserver.unobserve(element) }).pipe( mergeMap(() => intersectionObserverEntriesSubject.asObservable()), filter((entry: IntersectionObserverEntry) => entry.target === element), map((ev) => ({ isIntersecting: ev.isIntersecting, })), ) } } // This can be removed when intersection observer are supported by the browsers we support function createLegacyBased() { function getViewport() { return { left: 0, right: window.innerWidth, top: 0, bottom: window.innerHeight, } } function intersects( rect: DOMRect, viewport: {left: number; right: number; top: number; bottom: number}, margin: number, ) { return ( rect.left <= viewport.right + margin && rect.right >= viewport.left - margin && rect.top <= viewport.bottom + margin && rect.bottom >= viewport.top - margin ) } function inViewport(element: HTMLElement) { return () => intersects(element.getBoundingClientRect(), getViewport(), ROOT_MARGIN_PX) } // eslint-disable-next-line @typescript-eslint/no-shadow return function intersectionObservableFor(element: HTMLElement): Observable<IntersectionEvent> { const isElementInViewport = inViewport(element) return merge(observableOf(isElementInViewport()), resize$, scroll$, orientationChange$).pipe( // @todo: consider "faking" more of the IntersectionObserverEntry api if possible map(isElementInViewport), map((isIntersecting) => ({isIntersecting})), ) } }