|
1 | 1 | 'use client'
|
2 | 2 |
|
3 | 3 | 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' |
6 | 5 | import Link from 'next/link'
|
7 |
| -import { FaFacebook, FaFilePdf, FaLinkedin } from 'react-icons/fa' |
8 | 6 | import { DocTracingBeam } from './DocTracingBeam'
|
9 |
| -import { BASE_URL } from '@/lib/constants' |
10 |
| -import { title } from 'radash' |
11 | 7 | import { Button } from '../ui/button'
|
12 | 8 | import { GitHubIcon } from '../icons/GitHubIcon'
|
13 |
| - |
14 |
| -interface Props { |
15 |
| - doc: Doc |
16 |
| - articleRef?: React.RefObject<HTMLDivElement> |
17 |
| -} |
| 9 | +import { useMotionValue } from 'framer-motion' |
18 | 10 |
|
19 | 11 | interface Toc {
|
20 | 12 | url: string
|
21 | 13 | value: string
|
22 | 14 | depth: number
|
23 | 15 | }
|
24 | 16 |
|
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 | + }, []) |
27 | 55 |
|
28 | 56 | 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"> |
30 | 58 | <aside className="sticky top-[calc(var(--header-height)+1px+2rem)] max-h-[calc(100vh-var(--header-height)-3rem)] min-w-40 space-y-6">
|
31 | 59 | {doc.toc.length ? (
|
32 | 60 | <div className="relative flex flex-col">
|
33 | 61 | <p className="mb-2 font-mono text-sm uppercase dark:text-zinc-300">
|
34 | 62 | On this page
|
35 | 63 | </p>
|
36 |
| - <DocTracingBeam targetRef={articleRef}> |
| 64 | + <DocTracingBeam y1={y1} y2={y2}> |
37 | 65 | <ol className="flex flex-col gap-y-1 pl-4 text-sm font-medium">
|
38 | 66 | {doc.toc.map((item: Toc, i: number) => {
|
39 | 67 | return (
|
|
0 commit comments