diff --git a/src/components/NoteInteractions/Tabs.tsx b/src/components/NoteInteractions/Tabs.tsx index ada38a1..082fb9a 100644 --- a/src/components/NoteInteractions/Tabs.tsx +++ b/src/components/NoteInteractions/Tabs.tsx @@ -22,7 +22,8 @@ export function Tabs({ }) { const { t } = useTranslation() const tabRefs = useRef<(HTMLDivElement | null)[]>([]) - const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0 }) + const containerRef = useRef(null) + const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0, top: 0 }) // Filter tabs based on hideRepostsAndQuotes const visibleTabs = hideRepostsAndQuotes @@ -32,13 +33,24 @@ export function Tabs({ useEffect(() => { setTimeout(() => { const activeIndex = visibleTabs.findIndex((tab) => tab.value === selectedTab) - if (activeIndex >= 0 && tabRefs.current[activeIndex]) { + if (activeIndex >= 0 && tabRefs.current[activeIndex] && containerRef.current) { const activeTab = tabRefs.current[activeIndex] - const { offsetWidth, offsetLeft } = activeTab + const container = containerRef.current + const { offsetWidth, offsetLeft, offsetHeight } = activeTab + + // Get the container's top position relative to the viewport + const containerTop = container.getBoundingClientRect().top + const tabTop = activeTab.getBoundingClientRect().top + + // Calculate the indicator's top position relative to the container + // Position it at the bottom of the active tab's row + const relativeTop = tabTop - containerTop + offsetHeight const padding = 32 // 16px padding on each side + setIndicatorStyle({ width: offsetWidth - padding, - left: offsetLeft + padding / 2 + left: offsetLeft + padding / 2, + top: relativeTop - 4 // 4px for the indicator height (1px) + spacing }) } }, 20) // ensure tabs are rendered before calculating @@ -46,7 +58,7 @@ export function Tabs({ return (
-
+
{visibleTabs.map((tab, index) => (
))}
diff --git a/src/components/Tabs/index.tsx b/src/components/Tabs/index.tsx index a194510..2d1aa57 100644 --- a/src/components/Tabs/index.tsx +++ b/src/components/Tabs/index.tsx @@ -25,31 +25,43 @@ export default function Tabs({ const { deepBrowsing, lastScrollTop } = useDeepBrowsing() const tabRefs = useRef<(HTMLDivElement | null)[]>([]) const containerRef = useRef(null) - const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0 }) + const tabsContainerRef = useRef(null) + const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0, top: 0 }) const isUpdatingRef = useRef(false) - const lastStyleRef = useRef({ width: 0, left: 0 }) + const lastStyleRef = useRef({ width: 0, left: 0, top: 0 }) const updateIndicatorPosition = useCallback(() => { // Prevent multiple simultaneous updates if (isUpdatingRef.current) return const activeIndex = tabs.findIndex((tab) => tab.value === value) - if (activeIndex >= 0 && tabRefs.current[activeIndex]) { + if (activeIndex >= 0 && tabRefs.current[activeIndex] && tabsContainerRef.current) { const activeTab = tabRefs.current[activeIndex] - const { offsetWidth, offsetLeft } = activeTab + const tabsContainer = tabsContainerRef.current + const { offsetWidth, offsetLeft, offsetHeight } = activeTab const padding = 24 // 12px padding on each side + + // Get the container's top position relative to the viewport + const containerTop = tabsContainer.getBoundingClientRect().top + const tabTop = activeTab.getBoundingClientRect().top + + // Calculate the indicator's top position relative to the container + // Position it at the bottom of the active tab's row + const relativeTop = tabTop - containerTop + offsetHeight const newWidth = offsetWidth - padding const newLeft = offsetLeft + padding / 2 + const newTop = relativeTop - 4 // 4px for the indicator height (1px) + spacing // Only update if values actually changed if ( lastStyleRef.current.width !== newWidth || - lastStyleRef.current.left !== newLeft + lastStyleRef.current.left !== newLeft || + lastStyleRef.current.top !== newTop ) { isUpdatingRef.current = true - lastStyleRef.current = { width: newWidth, left: newLeft } + lastStyleRef.current = { width: newWidth, left: newLeft, top: newTop } - setIndicatorStyle({ width: newWidth, left: newLeft }) + setIndicatorStyle({ width: newWidth, left: newLeft, top: newTop }) // Reset flag after state update completes requestAnimationFrame(() => { @@ -70,7 +82,7 @@ export default function Tabs({ }, [updateIndicatorPosition]) useEffect(() => { - if (!containerRef.current) return + if (!containerRef.current || !tabsContainerRef.current) return const resizeObserver = new ResizeObserver(() => { requestAnimationFrame(() => { @@ -96,6 +108,10 @@ export default function Tabs({ tabRefs.current.forEach((tab) => { if (tab) resizeObserver.observe(tab) }) + + if (tabsContainerRef.current) { + resizeObserver.observe(tabsContainerRef.current) + } return () => { resizeObserver.disconnect() @@ -112,7 +128,7 @@ export default function Tabs({ )} >
-
+
{tabs.map((tab, index) => (
))}