Browse Source

fixed tab selection

imwald
Silberengel 4 months ago
parent
commit
6371b9253b
  1. 27
      src/components/NoteInteractions/Tabs.tsx
  2. 39
      src/components/Tabs/index.tsx

27
src/components/NoteInteractions/Tabs.tsx

@ -22,7 +22,8 @@ export function Tabs({ @@ -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<HTMLDivElement | null>(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({ @@ -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({ @@ -46,7 +58,7 @@ export function Tabs({
return (
<div className="w-full">
<div className="flex flex-wrap relative gap-1">
<div ref={containerRef} className="flex flex-wrap relative gap-1">
{visibleTabs.map((tab, index) => (
<div
key={tab.value}
@ -61,10 +73,11 @@ export function Tabs({ @@ -61,10 +73,11 @@ export function Tabs({
</div>
))}
<div
className="absolute bottom-0 h-1 bg-primary rounded-full transition-all duration-500"
className="absolute h-1 bg-primary rounded-full transition-all duration-500"
style={{
width: `${indicatorStyle.width}px`,
left: `${indicatorStyle.left}px`
left: `${indicatorStyle.left}px`,
top: `${indicatorStyle.top}px`
}}
/>
</div>

39
src/components/Tabs/index.tsx

@ -25,31 +25,43 @@ export default function Tabs({ @@ -25,31 +25,43 @@ export default function Tabs({
const { deepBrowsing, lastScrollTop } = useDeepBrowsing()
const tabRefs = useRef<(HTMLDivElement | null)[]>([])
const containerRef = useRef<HTMLDivElement | null>(null)
const [indicatorStyle, setIndicatorStyle] = useState({ width: 0, left: 0 })
const tabsContainerRef = useRef<HTMLDivElement | null>(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({ @@ -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(() => {
@ -97,6 +109,10 @@ export default function Tabs({ @@ -97,6 +109,10 @@ export default function Tabs({
if (tab) resizeObserver.observe(tab)
})
if (tabsContainerRef.current) {
resizeObserver.observe(tabsContainerRef.current)
}
return () => {
resizeObserver.disconnect()
intersectionObserver.disconnect()
@ -112,7 +128,7 @@ export default function Tabs({ @@ -112,7 +128,7 @@ export default function Tabs({
)}
>
<div className="flex-1 w-0">
<div className="flex flex-wrap relative gap-1">
<div ref={tabsContainerRef} className="flex flex-wrap relative gap-1">
{tabs.map((tab, index) => (
<div
key={tab.value}
@ -129,10 +145,11 @@ export default function Tabs({ @@ -129,10 +145,11 @@ export default function Tabs({
</div>
))}
<div
className="absolute bottom-0 h-1 bg-primary rounded-full transition-all duration-500"
className="absolute h-1 bg-primary rounded-full transition-all duration-500"
style={{
width: `${indicatorStyle.width}px`,
left: `${indicatorStyle.left}px`
left: `${indicatorStyle.left}px`,
top: `${indicatorStyle.top}px`
}}
/>
</div>

Loading…
Cancel
Save