Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Adding a Scroll-To-top Button #7339

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
144 changes: 100 additions & 44 deletions src/components/Layout/Toc.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,114 @@ import cx from 'classnames';
import {useTocHighlight} from './useTocHighlight';
import type {Toc} from '../MDX/TocContext';

import {useState, useEffect} from 'react';

function ScrollToTop() {
const [isVisible, setIsVisible] = useState(false);

// 控制按钮显示
const toggleVisibility = () => {
if (window.pageYOffset > 300) {
setIsVisible(true);
} else {
setIsVisible(false);
}
};

// 滚动到顶部
const scrollToTop = () => {
window.scrollTo({
top: 0,
behavior: 'smooth',
});
};

useEffect(() => {
window.addEventListener('scroll', toggleVisibility);
return () => {
window.removeEventListener('scroll', toggleVisibility);
};
}, []);

return (
<>
{isVisible && (
<div
onClick={scrollToTop}
className="fixed bottom-10 right-10 cursor-pointer bg-blue-500 rounded-full w-10 h-10 flex justify-center items-center shadow-lg transition-all duration-300 opacity-80 hover:opacity-100 hover:-translate-y-0.5 z-50">
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="white"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round">
<path d="M12 19V5M5 12l7-7 7 7" />
</svg>
</div>
)}
</>
);
}

export function Toc({headings}: {headings: Toc}) {
const {currentIndex} = useTocHighlight();
// TODO: We currently have a mismatch between the headings in the document
// and the headings we find in MarkdownPage (i.e. we don't find Recap or Challenges).
// Select the max TOC item we have here for now, but remove this after the fix.
const selectedIndex = Math.min(currentIndex, headings.length - 1);

return (
<nav role="navigation" className="pt-20 sticky top-0 end-0">
{headings.length > 0 && (
<h2 className="mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full">
On this page
</h2>
)}
<div
className="h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]"
style={{
overscrollBehavior: 'contain',
}}>
<ul className="space-y-2 pb-16">
{headings.length > 0 &&
headings.map((h, i) => {
if (!h.url && process.env.NODE_ENV === 'development') {
console.error('Heading does not have URL');
}
return (
<li
key={`heading-${h.url}-${i}`}
className={cx(
'text-sm px-2 rounded-s-xl',
selectedIndex === i
? 'bg-highlight dark:bg-highlight-dark'
: null,
{
'ps-4': h?.depth === 3,
hidden: h.depth && h.depth > 3,
}
)}>
<a
<>
<nav role="navigation" className="pt-20 sticky top-0 end-0">
{headings.length > 0 && (
<h2 className="mb-3 lg:mb-3 uppercase tracking-wide font-bold text-sm text-secondary dark:text-secondary-dark px-4 w-full">
On this page
</h2>
)}
<div
className="h-full overflow-y-auto ps-4 max-h-[calc(100vh-7.5rem)]"
style={{
overscrollBehavior: 'contain',
}}>
<ul className="space-y-2 pb-16">
{headings.length > 0 &&
headings.map((h, i) => {
if (!h.url && process.env.NODE_ENV === 'development') {
console.error('Heading does not have URL');
}
return (
<li
key={`heading-${h.url}-${i}`}
className={cx(
'text-sm px-2 rounded-s-xl',
selectedIndex === i
? 'text-link dark:text-link-dark font-bold'
: 'text-secondary dark:text-secondary-dark',
'block hover:text-link dark:hover:text-link-dark leading-normal py-2'
)}
href={h.url}>
{h.text}
</a>
</li>
);
})}
</ul>
</div>
</nav>
? 'bg-highlight dark:bg-highlight-dark'
: null,
{
'ps-4': h?.depth === 3,
hidden: h.depth && h.depth > 3,
}
)}>
<a
className={cx(
selectedIndex === i
? 'text-link dark:text-link-dark font-bold'
: 'text-secondary dark:text-secondary-dark',
'block hover:text-link dark:hover:text-link-dark leading-normal py-2'
)}
href={h.url}>
{h.text}
</a>
</li>
);
})}
</ul>
</div>
</nav>
<ScrollToTop />
</>
);
}
Loading