import { addMutationObserver, addResizeObserver } from '@wix/bex-core'; import { GridState } from './GridState'; import { EventEmitter } from 'events'; import { TypedEmitter } from '@wix/bex-core/events'; export interface VirtualTableRowMutatedStateProps { readonly state: GridState; readonly rowRef: { current: HTMLTableRowElement | null | undefined; }; readonly itemKey?: (index: number) => number | string; readonly index: number; readonly rowIndex?: number; } export class VirtualTableRowMutatedState { readonly state; rowRef; itemKey; index; rowIndex; readonly events = new EventEmitter() as TypedEmitter<{ disposeResizeObservers: (() => void) | undefined; }>; constructor(params: VirtualTableRowMutatedStateProps) { this.state = params.state; this.rowRef = params.rowRef; this.itemKey = params.itemKey; this.index = params.index; this.rowIndex = params.rowIndex; } _measureRowContentsHeight() { const { state, rowRef, itemKey, index } = this; const rowElement = rowRef.current; if ( rowElement == null || state.calcGridProps.rowHeight != null || itemKey == null || state.setVariableRowHeight == null ) { return; } rowElement.style.height = 'auto'; state.setVariableRowHeight({ key: itemKey(index), size: rowElement.offsetHeight, }); const totalHeight = state.calcGridProps.rowHeightIncludingGap({ index, key: itemKey(index), }); rowElement.style.height = `${totalHeight}px`; } setChildrenResizeObservers() { const { rowRef, state } = this; const { container: { lodash }, } = state; const rowElement = rowRef.current; if (!rowElement) { return; } const disposers = Array.from(rowElement.childNodes) .filter((td): td is HTMLElement => td instanceof HTMLElement) .map((td) => { return addResizeObserver( td, lodash.throttle(() => this._measureRowContentsHeight(), 100), ); }); return () => { disposers.forEach((disposer) => disposer?.()); }; } _disposeResizeObservers() { const { events } = this; const disposers = events.listeners('disposeResizeObservers'); events.removeAllListeners('disposeResizeObservers'); disposers.forEach((disposer) => disposer?.()); } init() { const { rowRef, events } = this; const rowElement = rowRef.current; if (!rowElement) { return; } const resetChildrenResizeObservers = () => { this._disposeResizeObservers(); events.on('disposeResizeObservers', this.setChildrenResizeObservers()); }; resetChildrenResizeObservers(); const disposers = [ addMutationObserver(rowElement, resetChildrenResizeObservers), ]; return () => { this._disposeResizeObservers(); disposers.forEach((disposer) => disposer?.()); }; } }