diff --git a/packages/mui-base/src/Tabs/Root/TabsRoot.tsx b/packages/mui-base/src/Tabs/Root/TabsRoot.tsx index 85b20c9838..eab967c43c 100644 --- a/packages/mui-base/src/Tabs/Root/TabsRoot.tsx +++ b/packages/mui-base/src/Tabs/Root/TabsRoot.tsx @@ -3,10 +3,11 @@ import * as React from 'react'; import PropTypes from 'prop-types'; import { useComponentRenderer } from '../../utils/useComponentRenderer'; import type { BaseUIComponentProps } from '../../utils/types'; -import { CompoundComponentContext } from '../../useCompound'; +import { CompositeList } from '../../Composite/List/CompositeList'; import { useTabsRoot } from './useTabsRoot'; import { TabsRootContext } from './TabsRootContext'; import { tabsStyleHookMapping } from './styleHooks'; +import { TabPanelMetadata } from '../TabPanel/useTabPanel'; /** * @@ -35,13 +36,14 @@ const TabsRoot = React.forwardRef(function TabsRoot( const { getRootProps, - compoundContext, direction, getTabId, - getTabPanelId, + getTabPanelIdByTabValueOrIndex, onSelected, registerTabIdLookup, + setTabPanelMap, tabActivationDirection, + tabPanelRefs, value, } = useTabsRoot({ value: valueProp, @@ -54,7 +56,7 @@ const TabsRoot = React.forwardRef(function TabsRoot( () => ({ direction, getTabId, - getTabPanelId, + getTabPanelIdByTabValueOrIndex, onSelected, orientation, registerTabIdLookup, @@ -64,7 +66,7 @@ const TabsRoot = React.forwardRef(function TabsRoot( [ direction, getTabId, - getTabPanelId, + getTabPanelIdByTabValueOrIndex, onSelected, orientation, registerTabIdLookup, @@ -90,11 +92,11 @@ const TabsRoot = React.forwardRef(function TabsRoot( }); return ( - - + + elementsRef={tabPanelRefs} onMapChange={setTabPanelMap}> {renderElement()} - - + + ); }); diff --git a/packages/mui-base/src/Tabs/Root/TabsRootContext.ts b/packages/mui-base/src/Tabs/Root/TabsRootContext.ts index dc910dae43..22df27ba6c 100644 --- a/packages/mui-base/src/Tabs/Root/TabsRootContext.ts +++ b/packages/mui-base/src/Tabs/Root/TabsRootContext.ts @@ -36,7 +36,7 @@ export interface TabsRootContext { * Gets the id of the tab panel with the given value. * @param value Value to find the tab panel for. */ - getTabPanelId: (value: any) => string | undefined; + getTabPanelIdByTabValueOrIndex: (tabValue: any, index: number) => string | undefined; /** * The position of the active tab relative to the previously active tab. */ diff --git a/packages/mui-base/src/Tabs/Root/useTabsRoot.ts b/packages/mui-base/src/Tabs/Root/useTabsRoot.ts index a1cf908be6..20cd527939 100644 --- a/packages/mui-base/src/Tabs/Root/useTabsRoot.ts +++ b/packages/mui-base/src/Tabs/Root/useTabsRoot.ts @@ -1,14 +1,9 @@ 'use client'; import * as React from 'react'; -import type { TabActivationDirection } from './TabsRoot'; -import { CompoundComponentContextValue, useCompoundParent } from '../../useCompound'; import { mergeReactProps } from '../../utils/mergeReactProps'; import { useControlled } from '../../utils/useControlled'; - -interface TabPanelMetadata { - id: string | undefined; - ref: React.RefObject; -} +import type { TabPanelMetadata } from '../TabPanel/useTabPanel'; +import type { TabActivationDirection } from './TabsRoot'; export interface TabMetadata { disabled: boolean; @@ -21,6 +16,9 @@ type IdLookupFunction = (id: any) => string | undefined; function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValue { const { value: valueProp, defaultValue, onValueChange, direction = 'ltr' } = parameters; + const tabPanelRefs = React.useRef<(HTMLElement | null)[]>([]); + const getTabIdByPanelValueRef = React.useRef(() => undefined); + const [value, setValue] = useControlled({ controlled: valueProp, default: defaultValue, @@ -28,6 +26,10 @@ function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValu state: 'value', }); + const [tabPanelMap, setTabPanelMap] = React.useState( + () => new Map(), + ); + const [tabActivationDirection, setTabActivationDirection] = React.useState('none'); @@ -44,26 +46,39 @@ function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValu [onValueChange, setValue], ); - const { subitems: tabPanels, contextValue: compoundComponentContextValue } = useCompoundParent< - any, - TabPanelMetadata - >(); - - const tabIdLookup = React.useRef(() => undefined); - - const getTabPanelId = React.useCallback( - (tabValue: any) => { - return tabPanels.get(tabValue)?.id; + const getTabPanelIdByTabValueOrIndex = React.useCallback( + (tabValue: any | undefined, index: number) => { + if (tabValue === undefined && index < 0) { + return undefined; + } + + for (const tabPanelMetadata of tabPanelMap.values()) { + // find by tabValue + if (tabValue !== undefined && tabPanelMetadata && tabValue === tabPanelMetadata?.value) { + return tabPanelMetadata.id; + } + + // find by index + if ( + tabValue === undefined && + tabPanelMetadata?.index && + tabPanelMetadata?.index === index + ) { + return tabPanelMetadata.id; + } + } + + return undefined; }, - [tabPanels], + [tabPanelMap], ); - const getTabId = React.useCallback((tabPanelId: any) => { - return tabIdLookup.current(tabPanelId); + const getTabIdByPanelValue = React.useCallback((tabPanelValue: any) => { + return getTabIdByPanelValueRef.current(tabPanelValue); }, []); const registerTabIdLookup = React.useCallback((lookupFunction: IdLookupFunction) => { - tabIdLookup.current = lookupFunction; + getTabIdByPanelValueRef.current = lookupFunction; }, []); const getRootProps: useTabsRoot.ReturnValue['getRootProps'] = React.useCallback( @@ -75,14 +90,15 @@ function useTabsRoot(parameters: useTabsRoot.Parameters): useTabsRoot.ReturnValu ); return { - compoundContext: compoundComponentContextValue, getRootProps, direction, - getTabId, - getTabPanelId, + getTabId: getTabIdByPanelValue, + getTabPanelIdByTabValueOrIndex, onSelected, registerTabIdLookup, + setTabPanelMap, tabActivationDirection, + tabPanelRefs, value, }; } @@ -113,7 +129,6 @@ namespace useTabsRoot { getRootProps: ( externalProps?: React.ComponentPropsWithRef<'div'>, ) => React.ComponentPropsWithRef<'div'>; - compoundContext: CompoundComponentContextValue; /** * The direction of the text. */ @@ -127,7 +142,10 @@ namespace useTabsRoot { * Gets the id of the tab panel with the given value. * @param value Value to find the tab panel for. */ - getTabPanelId: (value: any) => string | undefined; + getTabPanelIdByTabValueOrIndex: ( + tabValue: any | undefined, + tabIndex: number, + ) => string | undefined; /** * Callback for setting new value. @@ -141,10 +159,12 @@ namespace useTabsRoot { * Registers a function that returns the id of the tab with the given value. */ registerTabIdLookup: (lookupFunction: (id: any) => string | undefined) => void; + setTabPanelMap: (map: Map) => void; /** * The position of the active tab relative to the previously active tab. */ tabActivationDirection: TabActivationDirection; + tabPanelRefs: React.RefObject<(HTMLElement | null)[]>; /** * The currently selected tab's value. */ diff --git a/packages/mui-base/src/Tabs/Tab/Tab.test.tsx b/packages/mui-base/src/Tabs/Tab/Tab.test.tsx index 5a899d1fb5..13815c05d5 100644 --- a/packages/mui-base/src/Tabs/Tab/Tab.test.tsx +++ b/packages/mui-base/src/Tabs/Tab/Tab.test.tsx @@ -27,7 +27,7 @@ describe('', () => { onSelected() {}, registerTabIdLookup() {}, getTabId: () => '', - getTabPanelId: () => '', + getTabPanelIdByTabValueOrIndex: () => '', orientation: 'horizontal', direction: 'ltr', tabActivationDirection: 'none', diff --git a/packages/mui-base/src/Tabs/Tab/Tab.tsx b/packages/mui-base/src/Tabs/Tab/Tab.tsx index 805a95aa88..82565f465e 100644 --- a/packages/mui-base/src/Tabs/Tab/Tab.tsx +++ b/packages/mui-base/src/Tabs/Tab/Tab.tsx @@ -23,11 +23,15 @@ const Tab = React.forwardRef(function Tab( ) { const { className, disabled = false, render, value: valueProp, ...other } = props; - const { value: selectedValue, getTabPanelId, orientation } = useTabsRootContext(); + const { + value: selectedValue, + getTabPanelIdByTabValueOrIndex, + orientation, + } = useTabsRootContext(); const { selected, getRootProps } = useTab({ ...props, - getTabPanelId, + getTabPanelIdByTabValueOrIndex, isSelected: valueProp === selectedValue, rootRef: forwardedRef, }); diff --git a/packages/mui-base/src/Tabs/Tab/useTab.test.tsx b/packages/mui-base/src/Tabs/Tab/useTab.test.tsx index 451f1e6c49..a66721251d 100644 --- a/packages/mui-base/src/Tabs/Tab/useTab.test.tsx +++ b/packages/mui-base/src/Tabs/Tab/useTab.test.tsx @@ -15,7 +15,7 @@ describe('useTab', () => { const { getRootProps } = useTab({ rootRef, isSelected: true, - getTabPanelId: () => undefined, + getTabPanelIdByTabValueOrIndex: () => undefined, }); return