Skip to content

Commit

Permalink
chore: Refactor ScrollToTop component and add ScrollToTopButton
Browse files Browse the repository at this point in the history
The ScrollToTop component in src/components/ScrollToTop/ScrollToTop.tsx has been refactored to improve code readability and maintainability. Additionally, a new ScrollToTopButton component has been added to provide a button for scrolling to the top of the page. This change enhances the user experience and navigation within the application.
  • Loading branch information
tomek-i committed Aug 29, 2024
1 parent 026c182 commit 3ae4201
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 2 deletions.
22 changes: 20 additions & 2 deletions src/components/ScrollToTop/ScrollToTop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,36 @@ import { useLocation } from 'react-router';

export interface ScrollToTopProps extends React.PropsWithChildren {}

/**
* A component that scrolls to the element with the id of the `hash` property
* of the current location, or to the top of the page if no hash is present.
*
* This component is intended to be used with the `basename` property of the
* `Router` component from `react-router-dom` to handle client-side routing.
*
* @example
* import { BrowserRouter } from 'react-router-dom';
* import { ScrollToTop } from './ScrollToTop';
*
* const App = () => {
* return (
* <BrowserRouter basename="/my-app">
* <ScrollToTop />
* <Outlet />
* </BrowserRouter>
* );
* };
*/
export const ScrollToTop: React.FC<ScrollToTopProps> = () => {
const { pathname, hash } = useLocation();

useEffect(() => {
if (hash) {
// Scroll to the element with the specified hash
const element = document.getElementById(hash.substring(1));
if (element) {
element.scrollIntoView();
}
} else {
// Scroll to the top of the page
window.scrollTo(0, 0);
}
}, [pathname, hash]);
Expand Down
22 changes: 22 additions & 0 deletions src/components/ScrollToTop/ScrollToTopButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { useScrollVisibility } from '../hooks/useScrollVisibility';

const SCROLL_THRESHOLD = 20; // Percentage

export const ScrollToTopButton: React.FC = () => {
const isVisible = useScrollVisibility(SCROLL_THRESHOLD);

const handleScrollToTop = () => {
window.scrollTo({ top: 0, behavior: 'smooth' });
};

return (
<button
onClick={handleScrollToTop}
className={`fixed bottom-5 right-5 p-2.5 text-lg cursor-pointer z-50 bg-blue-500 text-white rounded transition-opacity duration-300 ${
isVisible ? 'opacity-100' : 'opacity-0 pointer-events-none'
}`}
>
Top
</button>
);
};
1 change: 1 addition & 0 deletions src/components/ScrollToTop/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './ScrollToTop';
export * from './ScrollToTopButton';
24 changes: 24 additions & 0 deletions src/components/hooks/useScrollVisibility.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { useEffect, useState } from 'react';

export const useScrollVisibility = (threshold: number) => {
const [isVisible, setIsVisible] = useState(false);

useEffect(() => {
const handleScroll = () => {
const scrollTop = window.scrollY;
const windowHeight = window.innerHeight;
const documentHeight = document.documentElement.scrollHeight;
const scrolledPercentage =
(scrollTop / (documentHeight - windowHeight)) * 100;

setIsVisible(scrolledPercentage > threshold);
};

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

return isVisible;
};
3 changes: 3 additions & 0 deletions src/layouts/default.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { ContactForm } from '../components/ContactForm/ContactForm';
import { useContactForm } from '../components/ContactForm/useContactForm';
import { Heading } from '../components/Header/Header';
import { Modal } from '../components/Modal/Modal';
import { ScrollToTopButton } from '../components/ScrollToTop';

export const Layout = () => {
const { setShowContactFormModal, showContactFormModal } = useContactForm();
Expand All @@ -26,6 +27,8 @@ export const Layout = () => {
document.body
)}
<Outlet />
</div>
<ScrollToTopButton />
</>
);
};

0 comments on commit 3ae4201

Please sign in to comment.