How to make this documentation index effect?

I'm struggling to do this right now, using framer-motion
25 Replies
Keef
Keef16mo ago
The source code is probably online if you check their github but looking at your video. I haven't looked at it but heres what I'm thinking: First look at the URL. As you scroll into sections its manipulating the URL and adding an id for the section you are scrolling into as you do it. The sidebar on the right is likely just doing some conditional rendering like active ? bg-purple : "" and the active case is just checking if the id is in the url You'll just need something to tell you when something is in view of the user and then add to your url. https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API Theres a hook for this on npm/all over the internet if you attach react/flavor of framework
Intersection Observer API - Web APIs | MDN
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
Keef
Keef16mo ago
Component in question
bmariano
bmarianoOP16mo ago
Well, no, scrolling doesn't manipulate the URL. It changes when you click the links. Actually, I could make this work, by doing what you said, checking if something is in the view. The problem is, when I click the link, it goes to the element, but some other elements are present in the viewport, so, that other element, changes the state and the link ends up being incorrect
bmariano
bmarianoOP16mo ago
bmariano
bmarianoOP16mo ago
Do you see?
Keef
Keef16mo ago
ACTUALLY YEAH IT DOES NOT MY BAD But it doesn't matter what method you interact with it the state is stored in that url and clicking it does the same thing
bmariano
bmarianoOP16mo ago
I'm feelling somehow stupid, but really I'm not being able to solve this lol
Keef
Keef16mo ago
Just dump the url stuff I mentioned And just check for inview I didn't realize you were clicking in your original post* Once the original "in view element" is out of view it seems to be changing on the astro docs so you could probably weight them? but I imagine their docs has a much cleaner approach then this
Keef
Keef16mo ago
Even their's is kinda buggy Sometimes the state doesnt reconcile correctly so you don't have any selected even when one is clearly in view hmm
bmariano
bmarianoOP16mo ago
I see examples like this everywhere, take a look at nextjs docs, it works similarly, there should be a concise way of doing this
Keef
Keef16mo ago
useEffect(() => {
const setCurrent: IntersectionObserverCallback = (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
const { id } = entry.target;
if (id === onThisPageID) continue;
setCurrentHeading({
slug: entry.target.id,
text: entry.target.textContent || '',
});
break;
}
}
};

const observerOptions: IntersectionObserverInit = {
// Negative top margin accounts for `scroll-margin`.
// Negative bottom margin means heading needs to be towards top of viewport to trigger intersection.
rootMargin: '-100px 0% -66%',
threshold: 1,
};

const headingsObserver = new IntersectionObserver(setCurrent, observerOptions);

// Observe all the headings in the main page content.
document.querySelectorAll('article :is(h1,h2,h3)').forEach((h) => headingsObserver.observe(h));

// Stop observing when the component is unmounted.
return () => headingsObserver.disconnect();
}, []);
useEffect(() => {
const setCurrent: IntersectionObserverCallback = (entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
const { id } = entry.target;
if (id === onThisPageID) continue;
setCurrentHeading({
slug: entry.target.id,
text: entry.target.textContent || '',
});
break;
}
}
};

const observerOptions: IntersectionObserverInit = {
// Negative top margin accounts for `scroll-margin`.
// Negative bottom margin means heading needs to be towards top of viewport to trigger intersection.
rootMargin: '-100px 0% -66%',
threshold: 1,
};

const headingsObserver = new IntersectionObserver(setCurrent, observerOptions);

// Observe all the headings in the main page content.
document.querySelectorAll('article :is(h1,h2,h3)').forEach((h) => headingsObserver.observe(h));

// Stop observing when the component is unmounted.
return () => headingsObserver.disconnect();
}, []);
This is the meat of it lets seeeee It really does look like they are doing the same thing from just examining the react state Idk where the next docs repo is So this is my cope Its also slightly buggy on the next site too sip
bmariano
bmarianoOP16mo ago
GitHub
next.js/docs/02-app/01-building-your-application at canary · vercel...
The React Framework. Contribute to vercel/next.js development by creating an account on GitHub.
bmariano
bmarianoOP16mo ago
Anyways, thanks man, if those are buggy too, i'll try to find a workaround
Keef
Keef16mo ago
Yeah I saw these but I was more looking for the actual code I just thought mdx is just markdown? thinkies
bmariano
bmarianoOP16mo ago
Yes, mdx allows you to use jsx in md files, I don't know it well
Keef
Keef16mo ago
Keef
Keef16mo ago
Its this
Keef
Keef16mo ago
Keef
Keef16mo ago
Intersection stuff
bmariano
bmarianoOP16mo ago
So, I think I made it. I just modified when the element is considered in view, to the 50% of it's height. Now, when you click the link, the element that is below is not considered inside the view, so it doesn't trigger the state change. I thought this when I just did it, but using px instead of % wasn't working so I had to look for other solutions
bmariano
bmarianoOP16mo ago
Keef
Keef16mo ago
Looks good. Are you trying to do transitions for scrolling to clicked sections?
bmariano
bmarianoOP16mo ago
Not necessarily, but I'll try
const ServicioContainer = ({
children,
className,
id,
}: {
children: React.ReactNode;
className?: string;
id: ServicioId;
}) => {

const { servicioActive, setServicioActive } = useServiciosStore();
const ref = useRef(null)
const isInView = useInView(ref, {
margin: '-50%'
})

useEffect(() => {
if (isInView) {
setServicioActive(id)
}
}
, [isInView])


return (
<div ref={ref} id={id} className={className}>
<div className="flex max-w-lg flex-col justify-between gap-6 sm:mx-auto sm:gap-8 lg:m-0 lg:max-w-none lg:flex-row xl:gap-24">
{children}
</div>
</div>
);
};
const ServicioContainer = ({
children,
className,
id,
}: {
children: React.ReactNode;
className?: string;
id: ServicioId;
}) => {

const { servicioActive, setServicioActive } = useServiciosStore();
const ref = useRef(null)
const isInView = useInView(ref, {
margin: '-50%'
})

useEffect(() => {
if (isInView) {
setServicioActive(id)
}
}
, [isInView])


return (
<div ref={ref} id={id} className={className}>
<div className="flex max-w-lg flex-col justify-between gap-6 sm:mx-auto sm:gap-8 lg:m-0 lg:max-w-none lg:flex-row xl:gap-24">
{children}
</div>
</div>
);
};
Anyways, this shouldn't be the best way, it works in this case. If there were elements with different heights, I'll not be good
Want results from more Discord servers?
Add your server