import { ScrollableWindow } from '../reach-bottom-listener'; import { EventEmitter, TypedEmitter } from '@wix/bex-core/events'; import { makeObservable, observable } from 'mobx'; import { PartialLodash } from '@wix/bex-core'; export interface ScrollPositionParams { container?: ScrollableWindow; // throttle scroll event delay?: number; readonly lodash: PartialLodash; } export interface ScrollState extends ScrollPositionParams {} export class ScrollState { public readonly events = new EventEmitter() as TypedEmitter<{ scroll: () => void; }>; private readonly scrollListener: (e: Event) => Promise | undefined; private _container?: ScrollableWindow; _scrollPosition: { scrollHeight: number; clientHeight: number; scrollTop: number; }; readonly lodash; constructor(params: ScrollPositionParams) { this.lodash = params.lodash; this._container = params.container; this._scrollPosition = this._container ? { scrollHeight: this._container.scrollHeight, clientHeight: this._container.clientHeight, scrollTop: this._container.scrollTop, } : { scrollHeight: 0, clientHeight: 0, scrollTop: 0, }; const delay = Math.max(params.delay || 0, 0); this.scrollListener = this.lodash.throttle(this.handleScroll, delay); makeObservable(this, { _scrollPosition: observable.ref }); } get scrollPosition() { return this._scrollPosition; } init(params: ScrollPositionParams): void | (() => void) { this._container = params.container ?? this._container; if (this._container == null) { return; } this._container.addEventListener('scroll', this.scrollListener); return () => { this._container!.removeEventListener('scroll', this.scrollListener); }; } handleScroll = async () => { const { clientHeight, scrollHeight, scrollTop } = this._container!; this._scrollPosition = { clientHeight, scrollHeight, scrollTop }; this.events.emit('scroll'); }; }