Need help with the Tab active state
so i have this 4 sections with the tab on the top of it. what i want to do is i want tomake the tabs dynamic they should be moving from Overviews to Class dates the tab's active state should also move dynamically i am using react and typescript and couldn't figure it out
reference of how i want it to work - https://relevel.com/test/business-development
1 Reply
this is done
just sharing of someone is looking for something similar
import { useEffect, useRef, useState } from "react";
import {
CourseOverview,
CourseClassSchedule,
CourseEducatorProfile,
CourseFaq,
} from "../components";
export function CoursePageNavTab() {
const overviewRef = useRef<HTMLDivElement>(null);
const classScheduleRef = useRef<HTMLDivElement>(null);
const educatorProfileRef = useRef<HTMLDivElement>(null);
const faqRef = useRef<HTMLDivElement>(null);
const overallTab = useRef<HTMLDivElement>(null);
const [activeTab, setActiveTab] = useState<'overview' | 'classSchedule' | 'educator' | 'faq'>('overview');
useEffect(() => {
const handleScroll = () => {
const scrollPosition = window.scrollY;
const classScheduleOffset = classScheduleRef.current?.offsetTop ?? 0;
const educatorProfileOffset = educatorProfileRef.current?.offsetTop ?? 0;
const faqOffset = faqRef.current?.offsetTop ?? 0;
if (scrollPosition < classScheduleOffset) {
setActiveTab('overview');
} else if (scrollPosition >= classScheduleOffset && scrollPosition < educatorProfileOffset) {
setActiveTab('classSchedule');
} else if (scrollPosition >= educatorProfileOffset && scrollPosition < faqOffset) {
setActiveTab('educator');
} else {
setActiveTab('faq');
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
const moveIndicator = (newTab: HTMLSpanElement) =>{
const currentTab = overallTab.current;
if (currentTab) {
const newTabWidth = newTab.offsetWidth / currentTab.offsetWidth ?? 0;
currentTab.style.setProperty("--_left", newTab.offsetLeft + "px");
currentTab.style.setProperty("--_width", newTabWidth.toString());
}
};
import { useEffect, useRef, useState } from "react";
import {
CourseOverview,
CourseClassSchedule,
CourseEducatorProfile,
CourseFaq,
} from "../components";
export function CoursePageNavTab() {
const overviewRef = useRef<HTMLDivElement>(null);
const classScheduleRef = useRef<HTMLDivElement>(null);
const educatorProfileRef = useRef<HTMLDivElement>(null);
const faqRef = useRef<HTMLDivElement>(null);
const overallTab = useRef<HTMLDivElement>(null);
const [activeTab, setActiveTab] = useState<'overview' | 'classSchedule' | 'educator' | 'faq'>('overview');
useEffect(() => {
const handleScroll = () => {
const scrollPosition = window.scrollY;
const classScheduleOffset = classScheduleRef.current?.offsetTop ?? 0;
const educatorProfileOffset = educatorProfileRef.current?.offsetTop ?? 0;
const faqOffset = faqRef.current?.offsetTop ?? 0;
if (scrollPosition < classScheduleOffset) {
setActiveTab('overview');
} else if (scrollPosition >= classScheduleOffset && scrollPosition < educatorProfileOffset) {
setActiveTab('classSchedule');
} else if (scrollPosition >= educatorProfileOffset && scrollPosition < faqOffset) {
setActiveTab('educator');
} else {
setActiveTab('faq');
}
};
window.addEventListener('scroll', handleScroll);
return () => window.removeEventListener('scroll', handleScroll);
}, []);
useEffect(() => {
const moveIndicator = (newTab: HTMLSpanElement) =>{
const currentTab = overallTab.current;
if (currentTab) {
const newTabWidth = newTab.offsetWidth / currentTab.offsetWidth ?? 0;
currentTab.style.setProperty("--_left", newTab.offsetLeft + "px");
currentTab.style.setProperty("--_width", newTabWidth.toString());
}
};
const tabSpans = overallTab.current?.querySelectorAll('span');
tabSpans?.forEach((span) => {
if(span.classList.contains('active')){
moveIndicator(span);
}
});
}, [activeTab]);
const scrollToRef = (ref: React.RefObject<HTMLDivElement>) => {
window.scrollTo({ top: (ref.current?.offsetTop ?? -500), behavior: 'smooth' });
};
return (
<section>
<nav className="navigate-course border-b border-[#ECECEC] pt-6 pb-3 sticky top-0 bg-white">
<div className="all-tab-container flex w-fit relative z-10 " ref={overallTab}>
<span
className={`px-3 py-1 relative ${activeTab === 'overview' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(overviewRef)}
>
Overview
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'classSchedule' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(classScheduleRef)}
>
Class Dates
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'educator' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(educatorProfileRef)}
>
Educator
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'faq' ? 'bg-[#212735] text-white ' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(faqRef)}
>
FAQs
</span>
</div>
</nav>
<CourseOverview ref={overviewRef} />
<CourseClassSchedule ref={classScheduleRef} />
<CourseEducatorProfile ref={educatorProfileRef} />
<CourseFaq ref={faqRef} />
</section>
);
}
const tabSpans = overallTab.current?.querySelectorAll('span');
tabSpans?.forEach((span) => {
if(span.classList.contains('active')){
moveIndicator(span);
}
});
}, [activeTab]);
const scrollToRef = (ref: React.RefObject<HTMLDivElement>) => {
window.scrollTo({ top: (ref.current?.offsetTop ?? -500), behavior: 'smooth' });
};
return (
<section>
<nav className="navigate-course border-b border-[#ECECEC] pt-6 pb-3 sticky top-0 bg-white">
<div className="all-tab-container flex w-fit relative z-10 " ref={overallTab}>
<span
className={`px-3 py-1 relative ${activeTab === 'overview' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(overviewRef)}
>
Overview
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'classSchedule' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(classScheduleRef)}
>
Class Dates
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'educator' ? 'bg-[#212735] text-white active' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(educatorProfileRef)}
>
Educator
</span>
<span
className={`px-3 py-1 relative ${activeTab === 'faq' ? 'bg-[#212735] text-white ' : ''} rounded-lg text-[14px] font-semibold`}
onClick={() => scrollToRef(faqRef)}
>
FAQs
</span>
</div>
</nav>
<CourseOverview ref={overviewRef} />
<CourseClassSchedule ref={classScheduleRef} />
<CourseEducatorProfile ref={educatorProfileRef} />
<CourseFaq ref={faqRef} />
</section>
);
}
.all-tab-container:after {
content: "";
position: absolute;
height: 4px;
border-radius: 7px 7px 0px 0px;
background-color: #212735;
bottom: -12px;
right: 0;
left: 0;
scale: var(--_width, 0.3) 1;
translate: var(--_left, 0px) 0;
transform-origin: left;
transition: scale 200ms, translate 200ms;
}
.all-tab-container:after {
content: "";
position: absolute;
height: 4px;
border-radius: 7px 7px 0px 0px;
background-color: #212735;
bottom: -12px;
right: 0;
left: 0;
scale: var(--_width, 0.3) 1;
translate: var(--_left, 0px) 0;
transform-origin: left;
transition: scale 200ms, translate 200ms;
}