@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.
146 lines (144 loc) • 5.59 kB
JavaScript
;
'use client';
var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TabsIndicator = void 0;
var React = _interopRequireWildcard(require("react"));
var _useForcedRerendering = require("@base-ui-components/utils/useForcedRerendering");
var _useOnMount = require("@base-ui-components/utils/useOnMount");
var _useRenderElement = require("../../utils/useRenderElement");
var _DirectionContext = require("../../direction-provider/DirectionContext");
var _TabsRootContext = require("../root/TabsRootContext");
var _stateAttributesMapping = require("../root/stateAttributesMapping");
var _TabsListContext = require("../list/TabsListContext");
var _prehydrationScript = require("./prehydrationScript.min");
var _TabsIndicatorCssVars = require("./TabsIndicatorCssVars");
var _jsxRuntime = require("react/jsx-runtime");
const stateAttributesMapping = {
..._stateAttributesMapping.tabsStateAttributesMapping,
activeTabPosition: () => null,
activeTabSize: () => null
};
/**
* A visual indicator that can be styled to match the position of the currently active tab.
* Renders a `<span>` element.
*
* Documentation: [Base UI Tabs](https://base-ui.com/react/components/tabs)
*/
const TabsIndicator = exports.TabsIndicator = /*#__PURE__*/React.forwardRef(function TabIndicator(componentProps, forwardedRef) {
const {
className,
render,
renderBeforeHydration = false,
...elementProps
} = componentProps;
const {
getTabElementBySelectedValue,
orientation,
tabActivationDirection,
value
} = (0, _TabsRootContext.useTabsRootContext)();
const {
tabsListElement
} = (0, _TabsListContext.useTabsListContext)();
const [isMounted, setIsMounted] = React.useState(false);
const {
value: activeTabValue
} = (0, _TabsRootContext.useTabsRootContext)();
const direction = (0, _DirectionContext.useDirection)();
(0, _useOnMount.useOnMount)(() => setIsMounted(true));
const rerender = (0, _useForcedRerendering.useForcedRerendering)();
React.useEffect(() => {
if (value != null && tabsListElement != null && typeof ResizeObserver !== 'undefined') {
const resizeObserver = new ResizeObserver(rerender);
resizeObserver.observe(tabsListElement);
return () => {
resizeObserver.disconnect();
};
}
return undefined;
}, [value, tabsListElement, 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 && tabsListElement != null) {
const activeTab = getTabElementBySelectedValue(value);
isTabSelected = true;
if (activeTab != null) {
const tabsListRect = tabsListElement.getBoundingClientRect();
const {
left: tabLeft,
top: tabTop,
width: computedWidth,
height: computedHeight
} = activeTab.getBoundingClientRect();
left = tabLeft - tabsListRect.left + tabsListElement.scrollLeft - tabsListElement.clientLeft;
top = tabTop - tabsListRect.top + tabsListElement.scrollTop - tabsListElement.clientTop;
width = computedWidth;
height = computedHeight;
right = direction === 'ltr' ? tabsListElement.scrollWidth - left - width - tabsListElement.clientLeft : left - tabsListElement.clientLeft;
bottom = tabsListElement.scrollHeight - top - height - tabsListElement.clientTop;
}
}
const activeTabPosition = React.useMemo(() => isTabSelected ? {
left,
right,
top,
bottom
} : null, [left, right, top, bottom, isTabSelected]);
const activeTabSize = React.useMemo(() => isTabSelected ? {
width,
height
} : null, [width, height, isTabSelected]);
const style = React.useMemo(() => {
if (!isTabSelected) {
return undefined;
}
return {
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabLeft]: `${left}px`,
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabRight]: `${right}px`,
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabTop]: `${top}px`,
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabBottom]: `${bottom}px`,
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabWidth]: `${width}px`,
[_TabsIndicatorCssVars.TabsIndicatorCssVars.activeTabHeight]: `${height}px`
};
}, [left, right, top, bottom, width, height, isTabSelected]);
const displayIndicator = isTabSelected && width > 0 && height > 0;
const state = React.useMemo(() => ({
orientation,
activeTabPosition,
activeTabSize,
tabActivationDirection
}), [orientation, activeTabPosition, activeTabSize, tabActivationDirection]);
const element = (0, _useRenderElement.useRenderElement)('span', componentProps, {
state,
ref: forwardedRef,
props: [{
role: 'presentation',
style,
hidden: !displayIndicator // do not display the indicator before the layout is settled
}, elementProps, {
suppressHydrationWarning: true
}],
stateAttributesMapping
});
if (activeTabValue == null) {
return null;
}
return /*#__PURE__*/(0, _jsxRuntime.jsxs)(React.Fragment, {
children: [element, !isMounted && renderBeforeHydration && /*#__PURE__*/(0, _jsxRuntime.jsx)("script", {
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML: {
__html: _prehydrationScript.script
},
suppressHydrationWarning: true
})]
});
});
if (process.env.NODE_ENV !== "production") TabsIndicator.displayName = "TabsIndicator";