diff --git a/packages/react/src/components/Tabs/Tabs.tsx b/packages/react/src/components/Tabs/Tabs.tsx index 69569268027e..744987a6e14b 100644 --- a/packages/react/src/components/Tabs/Tabs.tsx +++ b/packages/react/src/components/Tabs/Tabs.tsx @@ -565,6 +565,40 @@ function TabList({ } } + /** + * Scroll the tab into view if it is not already visible + * @param tab - The tab to scroll into view + * @returns {void} + */ + function scrollTabIntoView(tab) { + if (!isScrollable || !ref.current) { + return; + } + if (tab) { + // The width of the "scroll buttons" + const { width: tabWidth } = tab.getBoundingClientRect(); + + // The start and end position of the selected tab + const start = tab.offsetLeft; + const end = tab.offsetLeft + tabWidth; + + // The start and end of the visible area for the tabs + const visibleStart = ref.current.scrollLeft + buttonWidth; + const visibleEnd = + ref.current.scrollLeft + ref.current.clientWidth - buttonWidth; + + // The beginning of the tab is clipped and not visible + if (start < visibleStart) { + setScrollLeft(start - buttonWidth); + } + + // The end of the tab is clipped and not visible + if (end > visibleEnd) { + setScrollLeft(end + buttonWidth - ref.current.clientWidth); + } + } + } + useEffectOnce(() => { const tab = tabs.current[selectedIndex]; if (scrollIntoView && tab) { @@ -606,13 +640,13 @@ function TabList({ useIsomorphicEffect(() => { if (ref.current) { - //adding 1 in calculation for firefox support + // adding 1 in calculation for firefox support setIsScrollable(ref.current.scrollWidth > ref.current.clientWidth + 1); } function handler() { if (ref.current) { - //adding 1 in calculation for firefox support + // adding 1 in calculation for firefox support setIsScrollable(ref.current.scrollWidth > ref.current.clientWidth + 1); } } @@ -632,39 +666,21 @@ function TabList({ } }, [scrollLeft]); + // scroll manual tabs when active index changes (focus outline movement) useIsomorphicEffect(() => { - if (!isScrollable || !ref.current) { - return; - } - const tab = activation === 'manual' ? tabs.current[activeIndex] : tabs.current[selectedIndex]; - if (tab) { - // The width of the "scroll buttons" - - // The start and end position of the selected tab - const { width: tabWidth } = tab.getBoundingClientRect(); - const start = tab.offsetLeft; - const end = tab.offsetLeft + tabWidth; - - // The start and end of the visible area for the tabs - const visibleStart = ref.current.scrollLeft + buttonWidth; - const visibleEnd = - ref.current.scrollLeft + ref.current.clientWidth - buttonWidth; - // The beginning of the tab is clipped and not visible - if (start < visibleStart) { - setScrollLeft(start - buttonWidth); - } + scrollTabIntoView(tab); + }, [activation, activeIndex]); - // The end of the tab is clipped and not visible - if (end > visibleEnd) { - setScrollLeft(end + buttonWidth - ref.current.clientWidth); - } - } - }, [activation, activeIndex, selectedIndex, isScrollable, children]); + // scroll tabs when selected index changes + useIsomorphicEffect(() => { + const tab = tabs.current[selectedIndex]; + scrollTabIntoView(tab); + }, [selectedIndex, isScrollable, children]); usePressable(previousButton, { onPress({ longPress }) {