UNPKG

@base-ui-components/react

Version:

Base UI is a library of headless ('unstyled') React components and low-level hooks. You gain complete control over your app's CSS and accessibility features.

91 lines (90 loc) 2.9 kB
'use client'; import * as React from 'react'; import { mergeReactProps } from '../../utils/mergeReactProps.js'; import { useForcedRerendering } from '../../utils/useForcedRerendering.js'; import { TabsIndicatorCssVars } from './TabsIndicatorCssVars.js'; function round(value) { return Math.round(value * 100) * 0.01; } export function useTabsIndicator(parameters) { const { value, tabsListRef, getTabElementBySelectedValue } = parameters; const rerender = useForcedRerendering(); React.useEffect(() => { if (value != null && tabsListRef.current != null && typeof ResizeObserver !== 'undefined') { const resizeObserver = new ResizeObserver(() => { rerender(); }); resizeObserver.observe(tabsListRef.current); return () => { resizeObserver.disconnect(); }; } return undefined; }, [value, tabsListRef, rerender]); let left = 0; let right = 0; let top = 0; let bottom = 0; let width = 0; let height = 0; let isTabSelected = false; if (value != null && tabsListRef.current != null) { const selectedTabElement = getTabElementBySelectedValue(value); isTabSelected = true; if (selectedTabElement != null) { const { left: tabLeft, right: tabRight, bottom: tabBottom, top: tabTop } = selectedTabElement.getBoundingClientRect(); const { left: listLeft, right: listRight, top: listTop, bottom: listBottom } = tabsListRef.current.getBoundingClientRect(); left = round(tabLeft - listLeft); right = round(listRight - tabRight); top = round(tabTop - listTop); bottom = round(listBottom - tabBottom); width = round(tabRight - tabLeft); height = round(tabBottom - tabTop); } } const activeTabPosition = React.useMemo(() => isTabSelected ? { left, right, top, bottom } : null, [left, right, top, bottom, isTabSelected]); const style = React.useMemo(() => { if (!isTabSelected) { return undefined; } return { [TabsIndicatorCssVars.activeTabLeft]: `${left}px`, [TabsIndicatorCssVars.activeTabRight]: `${right}px`, [TabsIndicatorCssVars.activeTabTop]: `${top}px`, [TabsIndicatorCssVars.activeTabBottom]: `${bottom}px`, [TabsIndicatorCssVars.activeTabWidth]: `${width}px`, [TabsIndicatorCssVars.activeTabHeight]: `${height}px` }; }, [left, right, top, bottom, width, height, isTabSelected]); const displayIndicator = isTabSelected && width > 0 && height > 0; const getRootProps = React.useCallback((externalProps = {}) => { return mergeReactProps(externalProps, { role: 'presentation', style, hidden: !displayIndicator // do not display the indicator before the layout is settled }); }, [style, displayIndicator]); return { getRootProps, activeTabPosition }; }