import { action, makeObservable, observable } from 'mobx'; import { addResizeObserver, WixPatternsContainer, isMobileDevice, } from '@wix/bex-core'; export interface TabsResponsiveStateParams { container: WixPatternsContainer; } export class TabsResponsiveState { readonly isMobile: boolean; container?: HTMLDivElement; // We save the badges in a Map and not in an Array because the badge is optional // on a per-tab basis, but we still need to know the index of each badge. badges = new Map(); mode: 'tabs' | 'dropdown' = 'tabs'; panelMinWidth = 0; itemsCount = 0; itemPadding = 32; itemMaxWidth = 200; itemTextMinWidth = 48; itemBadgeMargin = 6; itemsMinWidths: number[] = []; readonly onResizeThrottled; constructor(params: TabsResponsiveStateParams) { this.isMobile = isMobileDevice(); this.onResizeThrottled = params.container.lodash.throttle( () => window.requestAnimationFrame(this._onResize), 300, ); makeObservable(this, { mode: observable.ref, panelMinWidth: observable.ref, }); } getBadgesWidth() { return Array.from(this.badges.values()).reduce((sum, badge) => { return sum + badge.clientWidth + this.itemBadgeMargin; }, 0); } getItemsMinWidth() { const { itemTextMinWidth, itemBadgeMargin } = this; return Array.from({ length: this.itemsCount }).map((_, index) => { const badge = this.badges.get(index); const maybeBadgeWidth = badge ? badge.clientWidth + itemBadgeMargin : 0; return itemTextMinWidth + maybeBadgeWidth; }); } getPanelMinWidth() { const { itemsCount, itemTextMinWidth, itemPadding } = this; const itemMinWidth = itemTextMinWidth + itemPadding; const badgesWidth = this.getBadgesWidth(); return itemsCount * itemMinWidth + badgesWidth; } _externalAvailableWidth: number | null = null; setAvailableWidth(width: number) { this._externalAvailableWidth = width; this._onResize(); } _onResize = action(() => { const { container } = this; const availableWidth = this._externalAvailableWidth ?? container?.clientWidth ?? 0; if (process.env.NODE_ENV === 'test') { return; } if (!container) { return; } if (this.isMobile) { this.mode = 'dropdown'; return; } const nextPanelWidth = this.getPanelMinWidth(); if (nextPanelWidth !== 0 && nextPanelWidth > this.panelMinWidth) { this.itemsMinWidths = this.getItemsMinWidth(); this.panelMinWidth = nextPanelWidth; } if (this.panelMinWidth !== 0 && availableWidth < this.panelMinWidth) { this.mode = 'dropdown'; } else { this.mode = 'tabs'; } }); init({ container, itemsCount, }: { container?: HTMLDivElement; itemsCount: number; }) { this.container = container; this.itemsCount = itemsCount; const disposer = addResizeObserver(container, this.onResizeThrottled); this.onResizeThrottled(); return () => { disposer?.(); }; } }