import React, { useEffect, useState } from 'react'; import styled from 'styled-components'; import v from '../../styles/Variables'; interface Props { /** * The icon or words that will show on the button, must be wrapped in valid JSX like a span */ delimiter?: string; /** * Classes to be applied to the delimiter itself (wrapped in a span) */ delimiterTheme?: string; /** * When done scrolling, where focus should go for screen readers */ focusEl?: string; /** * Position from the right */ right: number; /** * The height and width of the button */ size?: number; /** * An ID of where to scroll to, if excluded, will be the top of the page */ target?: string | null; /** * Classes applied to the button wrapper */ theme?: string; /** * How far the user has to scroll before the button shows, if excluded, it will always show */ threshold?: number; } interface Style { size?: number; right?: number; } const ScrollToTop = ({ delimiter = '↑', delimiterTheme, focusEl = '#app', right = 40, size = 40, target, theme, threshold = 100 }: Props) => { const [show, showSetter] = useState(threshold ? false : true); useEffect(() => { if (window) window.addEventListener('scroll', () => { if (threshold && window.scrollY > threshold) { showSetter(true); } else { showSetter(false); } }); }, [threshold]); const scrollToTop = () => { if (!target) { window.scrollTo({ top: 0, behavior: 'smooth' }); } else { const elmnt = document.getElementById(target); if (elmnt) elmnt.scrollIntoView({ behavior: 'smooth' }); } }; return ( {show === true && ( { scrollToTop(); const el = document.querySelector(focusEl) as HTMLElement; if (el !== null) el.focus(); }} size={size} right={right} aria-label="Scroll back to top" > {delimiter} )} ); }; export default ScrollToTop; /* styles */ const STT = styled.button