sticky tab improvements

This commit is contained in:
yohlo
2025-08-24 23:52:32 -05:00
parent e8bb6a5a36
commit a413d4421b

View File

@@ -22,6 +22,8 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
const [isSticky, setIsSticky] = useState(false);
const tabsRef = useRef<HTMLDivElement | null>(null);
const originalPositionRef = useRef<number | null>(null);
const slideRefs = useRef<Record<number, HTMLDivElement | null>>({});
const [activeSlideHeight, setActiveSlideHeight] = useState<number | 'auto'>('auto');
const stickyThreshold = 0;
useEffect(() => {
@@ -30,6 +32,13 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
const onSelect = () => {
const newIndex = embla.selectedScrollSnap();
setActiveTab(newIndex);
// Update height based on active slide content
const activeSlideRef = slideRefs.current[newIndex];
if (activeSlideRef) {
const height = activeSlideRef.scrollHeight;
setActiveSlideHeight(height);
}
};
embla.on("select", onSelect);
@@ -39,6 +48,15 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
};
}, [embla]);
// Update height when activeTab changes
useEffect(() => {
const activeSlideRef = slideRefs.current[activeTab];
if (activeSlideRef) {
const height = activeSlideRef.scrollHeight;
setActiveSlideHeight(height);
}
}, [activeTab]);
useEffect(() => {
if (scrollPosition) {
setIsSticky(scrollPosition.y > stickyThreshold);
@@ -86,6 +104,10 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
setControlsRefs(controlsRefs);
};
const setSlideRef = (index: number) => (node: HTMLDivElement | null) => {
slideRefs.current[index] = node;
};
return (
<Box>
{isSticky && (
@@ -130,7 +152,7 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
? 'var(--mantine-color-blue-6)'
: 'var(--mantine-color-text)',
fontWeight: activeTab === index ? 600 : 400,
transition: 'color 200ms ease, font-weight 200ms ease',
transition: 'color 200ms ease',
backgroundColor: 'transparent',
border: 'none',
borderRadius: 0,
@@ -160,12 +182,21 @@ function SwipeableTabs({ tabs, defaultTab = 0, onTabChange, scrollPosition }: Sw
slideSize="100%"
initialSlide={activeTab}
style={{
overflow: 'hidden'
overflow: 'hidden',
height: activeSlideHeight === 'auto' ? 'auto' : `${activeSlideHeight}px`,
transition: 'height 300ms ease'
}}
>
{tabs.map((tab, index) => (
<Carousel.Slide key={`${tab.label}-content-${index}`}>
<Box>
<Carousel.Slide key={`${tab.label}-content-${index}`} style={{ height: 'auto' }}>
<Box
ref={setSlideRef(index)}
style={{
minHeight: 'fit-content',
height: 'auto',
visibility: index === activeTab ? 'visible' : 'hidden'
}}
>
{tab.content}
</Box>
</Carousel.Slide>