Skip to content
This repository was archived by the owner on May 20, 2025. It is now read-only.

Commit a80738c

Browse files
scroll only when headings have been reached
1 parent 0cdfe3e commit a80738c

File tree

3 files changed

+54
-40
lines changed

3 files changed

+54
-40
lines changed

src/app/[[...slug]]/layout.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { Prose } from '@/components/Prose'
33
import React from 'react'
44
import { allDocs } from '@/content'
55
import DocToc from '@/components/layout/DocToC'
6-
import { Feedback } from '@/components/Feedback'
76
import { Button } from '@/components/ui/button'
87
import { GitHubIcon } from '@/components/icons/GitHubIcon'
98
import Breadcrumbs from '@/components/Breadcrumbs'

src/components/layout/DocToC.tsx

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,67 @@
11
'use client'
22

33
import type { Doc } from '@/content'
4-
import React, { useRef } from 'react'
5-
import { Badge } from '../ui/badge'
4+
import { useRef, useLayoutEffect, useState, useEffect } from 'react'
65
import Link from 'next/link'
7-
import { FaFacebook, FaFilePdf, FaLinkedin } from 'react-icons/fa'
86
import { DocTracingBeam } from './DocTracingBeam'
9-
import { BASE_URL } from '@/lib/constants'
10-
import { title } from 'radash'
117
import { Button } from '../ui/button'
128
import { GitHubIcon } from '../icons/GitHubIcon'
13-
14-
interface Props {
15-
doc: Doc
16-
articleRef?: React.RefObject<HTMLDivElement>
17-
}
9+
import { useMotionValue } from 'framer-motion'
1810

1911
interface Toc {
2012
url: string
2113
value: string
2214
depth: number
2315
}
2416

25-
const DocToC: React.FC<Props> = ({ doc }) => {
26-
const articleRef = useRef<HTMLDivElement>(null)
17+
const DocToC = ({ doc }: { doc: Doc }) => {
18+
const initial = 14
19+
const sectionSize = 28
20+
const offset = 10
21+
22+
const y1 = useMotionValue(0)
23+
const y2 = useMotionValue(0)
24+
25+
useEffect(() => {
26+
const headings = document
27+
.querySelectorAll<HTMLHeadingElement>('.md-content-header')
28+
.values()
29+
.toArray()
30+
31+
const options = {
32+
root: null,
33+
rootMargin: '0px',
34+
threshold: 1, // Adjust based on when you want to highlight
35+
}
36+
37+
const observer = new IntersectionObserver((entries) => {
38+
entries.forEach((entry) => {
39+
if (entry.isIntersecting) {
40+
const index = headings.findIndex(
41+
(heading) => heading.textContent === entry.target.textContent,
42+
)
43+
44+
y2.set(initial + (index * sectionSize + offset))
45+
}
46+
})
47+
}, options)
48+
49+
headings.forEach((h2) => observer.observe(h2))
50+
51+
return () => {
52+
headings.forEach((h2) => observer.unobserve(h2))
53+
}
54+
}, [])
2755

2856
return (
29-
<div className="hidden h-full min-w-52 md:block" ref={articleRef}>
57+
<div className="hidden h-full min-w-52 md:block">
3058
<aside className="sticky top-[calc(var(--header-height)+1px+2rem)] max-h-[calc(100vh-var(--header-height)-3rem)] min-w-40 space-y-6">
3159
{doc.toc.length ? (
3260
<div className="relative flex flex-col">
3361
<p className="mb-2 font-mono text-sm uppercase dark:text-zinc-300">
3462
On this page
3563
</p>
36-
<DocTracingBeam targetRef={articleRef}>
64+
<DocTracingBeam y1={y1} y2={y2}>
3765
<ol className="flex flex-col gap-y-1 pl-4 text-sm font-medium">
3866
{doc.toc.map((item: Toc, i: number) => {
3967
return (

src/components/layout/DocTracingBeam.tsx

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
11
'use client'
22

33
import React, { useEffect, useRef, useState } from 'react'
4-
import { motion, useTransform, useScroll, useSpring } from 'framer-motion'
4+
import {
5+
motion,
6+
useTransform,
7+
useSpring,
8+
useMotionValue,
9+
MotionValue,
10+
} from 'framer-motion'
511
import { cn } from '@/lib/utils'
612

7-
// TODO this should set the height based off the h2 offset heights, not current scroll progress
813
export const DocTracingBeam = ({
914
children,
1015
className,
11-
targetRef,
16+
y1,
17+
y2,
1218
}: {
1319
children: React.ReactNode
1420
className?: string
15-
targetRef: React.RefObject<HTMLDivElement>
21+
y1: MotionValue<number>
22+
y2: MotionValue<number>
1623
}) => {
17-
const { scrollYProgress } = useScroll({
18-
target: targetRef,
19-
offset: ['start start', 'end end'],
20-
})
21-
2224
const contentRef = useRef<HTMLDivElement>(null)
2325
const [svgHeight, setSvgHeight] = useState(0)
2426

@@ -28,21 +30,6 @@ export const DocTracingBeam = ({
2830
}
2931
}, [])
3032

31-
const y1 = useSpring(
32-
useTransform(scrollYProgress, [0, 0.8], [0, svgHeight]),
33-
{
34-
stiffness: 500,
35-
damping: 90,
36-
},
37-
)
38-
const y2 = useSpring(
39-
useTransform(scrollYProgress, [0, 1], [0, svgHeight - 210]),
40-
{
41-
stiffness: 500,
42-
damping: 90,
43-
},
44-
)
45-
4633
return (
4734
<motion.div
4835
className={cn('relative mx-auto h-full w-full max-w-4xl', className)}
@@ -81,7 +68,7 @@ export const DocTracingBeam = ({
8168
x1="0"
8269
x2="0"
8370
y1={y1} // set y1 for gradient
84-
y2={y2} // set y2 for gradient
71+
y2={useSpring(y2)} // set y2 for gradient
8572
>
8673
<stop stopColor="var(--primary-500)" stopOpacity="0"></stop>
8774
<stop stopColor="var(--primary-500)"></stop>

0 commit comments

Comments
 (0)