Last active
May 11, 2020 01:10
-
-
Save mlynch/d49ffe3df347dbed50a239f5158dd1f3 to your computer and use it in GitHub Desktop.
Scroll Spy Provider
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Heading = (props: Props) => { | |
const ref = useRef<HTMLHeadingElement>(); | |
const ScrollSpy = useContext(ScrollSpyContext); | |
if ( | |
ref.current && | |
["H1", "H2", "H3"].indexOf(ref.current.tagName) >= 0 | |
) { | |
value?.observer!.observe(ref.current); | |
} | |
// .... | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useEffect, useCallback, createContext, useState, useRef } from "react"; | |
interface Props { | |
children: React.ReactNode; | |
} | |
type ScrollSpyValue = { | |
observer: IntersectionObserver | null; | |
visible: Element[]; | |
}; | |
const ScrollSpyContext = createContext<ScrollSpyValue | null>(null); | |
const ScrollSpyProvider = ({ children }: Props) => { | |
const [observer, setObserver] = useState<IntersectionObserver | null>(null); | |
const [visible, setVisible] = useState<Element[]>([]); | |
let value = {observer, visible}; | |
const handleIntersection = useCallback( | |
(entries: IntersectionObserverEntry[], observer: IntersectionObserver) => { | |
entries.forEach(e => { | |
if (e.intersectionRatio > 0) { | |
setVisible(prevVisible => [...prevVisible, e.target]); | |
} else { | |
setVisible(prevVisible => prevVisible.filter(v => v !== e.target)); | |
} | |
}) | |
}, | |
[visible, setVisible] | |
); | |
useEffect(() => { | |
const observer = new IntersectionObserver(handleIntersection, { | |
threshold: 0, | |
}); | |
setObserver(observer); | |
return () => { | |
observer.disconnect(); | |
}; | |
}, []); | |
return ( | |
<ScrollSpyContext.Provider value={value}> | |
{children} | |
</ScrollSpyContext.Provider> | |
); | |
}; | |
const ScrollSpyConsumer = ScrollSpyContext.Consumer; | |
export { ScrollSpyContext, ScrollSpyProvider, ScrollSpyConsumer }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const TableOfContents = ({ doc }: TableOfContentsProps) => { | |
const headings = getHeadings(doc); | |
const ScrollSpy = useContext(ScrollSpyContext); | |
return ( | |
<div> | |
// Check ScrollSpy.visible against your child elements | |
// and activate one if it matches. | |
// For example, I'm looping through each of my known headings | |
// and activating the first one that matches in value.visible | |
// and keeping the last activated one in case none match | |
}} | |
</div> | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment