docu v 1.11.0

This commit is contained in:
Wildan Nursahidan
2025-05-26 23:03:58 +07:00
parent e25ee4cb93
commit 3da46325cf
57 changed files with 1433 additions and 4182 deletions

2
hooks/index.ts Normal file
View File

@@ -0,0 +1,2 @@
export * from './useScrollPosition';
export * from './useActiveSection';

68
hooks/useActiveSection.ts Normal file
View File

@@ -0,0 +1,68 @@
import { useState, useCallback, useEffect, useRef } from 'react';
import { TocItem } from '@/lib/toc';
export function useActiveSection(tocs: TocItem[]) {
const [activeId, setActiveId] = useState<string | null>(null);
const observerRef = useRef<IntersectionObserver | null>(null);
const clickedIdRef = useRef<string | null>(null);
// Handle intersection observer for active section
useEffect(() => {
if (typeof document === 'undefined' || !tocs.length) return;
const handleIntersect = (entries: IntersectionObserverEntry[]) => {
if (clickedIdRef.current) return;
const visibleEntries = entries.filter(entry => entry.isIntersecting);
if (!visibleEntries.length) return;
// Find the most visible entry
const mostVisibleEntry = visibleEntries.reduce((prev, current) => {
return current.intersectionRatio > prev.intersectionRatio ? current : prev;
}, visibleEntries[0]);
const newActiveId = mostVisibleEntry.target.id;
if (newActiveId !== activeId) {
setActiveId(newActiveId);
}
};
// Initialize intersection observer
observerRef.current = new IntersectionObserver(handleIntersect, {
root: null,
rootMargin: '0px 0px -80% 0px',
threshold: 0.1,
});
// Observe all headings
tocs.forEach(toc => {
const element = document.getElementById(toc.href.slice(1));
if (element) {
observerRef.current?.observe(element);
}
});
// Cleanup
return () => {
observerRef.current?.disconnect();
};
}, [tocs, activeId]);
const handleLinkClick = useCallback((id: string) => {
clickedIdRef.current = id;
setActiveId(id);
// Reset clicked state after scroll completes
const timer = setTimeout(() => {
clickedIdRef.current = null;
}, 1000);
return () => clearTimeout(timer);
}, []);
return {
activeId,
setActiveId,
handleLinkClick,
};
}

View File

@@ -0,0 +1,28 @@
import { useState, useCallback, useEffect } from 'react';
export function useScrollPosition(threshold = 0.5) {
const [isScrolled, setIsScrolled] = useState(false);
const handleScroll = useCallback(() => {
if (typeof window === 'undefined') return;
const scrollPosition = window.scrollY;
const viewportHeight = window.innerHeight;
const shouldBeSticky = scrollPosition > viewportHeight * threshold;
setIsScrolled(prev => shouldBeSticky !== prev ? shouldBeSticky : prev);
}, [threshold]);
// Add scroll event listener
useEffect(() => {
// Initial check
handleScroll();
window.addEventListener('scroll', handleScroll, { passive: true });
return () => {
window.removeEventListener('scroll', handleScroll);
};
}, [handleScroll]);
return isScrolled;
}