import { action, makeObservable, observable } from 'mobx'; import { addResizeObserver, WixPatternsContainer } from '@wix/bex-core'; export interface Rect { bottom: number; height: number; left: number; right: number; top: number; width: number; // x: number; // y: number; } export interface EmptyRect { bottom: undefined; height: undefined; left: undefined; right: undefined; top: undefined; width: undefined; // x: undefined; // y: undefined; } export const emptyRect = () => ({ bottom: undefined, height: undefined, left: undefined, right: undefined, top: undefined, width: undefined, // x: undefined, // y: undefined, }); export interface RectStateParams { container: WixPatternsContainer; } export class RectState { readonly _syncThrottled; readonly _syncDebounced; readonly container; element?: Element | null | undefined; readonly rect = observable(emptyRect()); constructor(params: RectStateParams) { this.container = params.container; makeObservable(this, { _sync: action.bound, }); this._syncThrottled = this.container.lodash.throttle(() => { window.requestAnimationFrame(this._sync); }, 200); this._syncDebounced = () => window.requestAnimationFrame(this._sync); } _readRect() { const { element, container: { window }, } = this; if (element == null) { return {}; } if (process.env.NODE_ENV === 'test') { const { matches } = window.matchMedia('(min-width: 768px)'); return matches ? { height: 600, width: 840, top: 0, bottom: 0, right: 0, left: 0, y: 0, x: 0, } : { height: 600, width: 610, top: 0, bottom: 0, right: 0, left: 0, y: 0, x: 0, }; } const r = element.getBoundingClientRect(); return { bottom: r.bottom, height: r.height, left: r.left, right: r.right, top: r.top, width: r.width, x: r.x, y: r.y, }; } _sync() { const { rect } = this; Object.assign(rect, this._readRect()); } init({ scrollElement }: { scrollElement?: Element | null } = {}) { const { element } = this; if (element) { this._syncThrottled(); if (scrollElement) { scrollElement.addEventListener('scroll', this._syncThrottled); } const disposers = [addResizeObserver(element, this._syncDebounced)]; return action(() => { disposers.forEach((d) => d?.()); Object.assign(this.rect, emptyRect()); if (scrollElement) { scrollElement.removeEventListener('scroll', this._syncThrottled); } }); } return undefined; } }